<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Srivats P</title>
    <description>Personal website of Srivats P - programmer, runner, creator</description>
    <atom:link href="https://srivatsp.com/feed-rss.xml" rel="self" type="application/rss+xml" />
    <link>https://srivatsp.com</link>
    <managingEditor>me@srivatsp.com (Srivats P)</managingEditor>
    <pubDate>Tue, 03 Mar 2026 13:41:00 +0000</pubDate>
  
    
	<item>
          <title>Enabling extra repos for RHEL on OBS</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;It took me a bit of time and effort to figure this out, so thought I’d post it for other people’s benefit (and for future-forgetful-me!).&lt;/p&gt;

&lt;p&gt;If you use Open Build Service (OBS) to build Linux RPM packages on RHEL-compatible distros like Alma Linux or Rocky Linux and your builds fail due to dependencies not being found, read on. This &lt;em&gt;might&lt;/em&gt; help.&lt;/p&gt;

&lt;p&gt;Starting RHEL 8, RedHat (and clones) shifted to a multi-repo layout for packages named BaseOS, CodeReady Builder (CRB), AppStream etc. Packages are now divided between these repos.&lt;/p&gt;

&lt;p&gt;By default OBS only enables the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;baseos&lt;/code&gt; repo for these distros. To enable other repos (including the Fedora EPEL) on your OBS project, you need to add them to your OBS project’s Meta -&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;home:pstavirs&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  . . .
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;repository&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AlmaLinux_9&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AlmaLinux:9&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;repository=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;baseos&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AlmaLinux:9&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;repository=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;appstream&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AlmaLinux:9&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;repository=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CRB&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fedora:EPEL:9&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;repository=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;standard&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;arch&amp;gt;&lt;/span&gt;x86_64&lt;span class=&quot;nt&quot;&gt;&amp;lt;/arch&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are using a local OBS instance with interconnect enabled to the public OBS instance, just prefix the local project name of the interconnect OBS instance -&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;repository&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AlmaLinux_10&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;openSUSE.org:AlmaLinux:10&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;repository=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;baseos&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;openSUSE.org:AlmaLinux:10&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;repository=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;appstream&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;project=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;openSUSE.org:AlmaLinux:10&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;repository=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CRB&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;arch&amp;gt;&lt;/span&gt;x86_64&lt;span class=&quot;nt&quot;&gt;&amp;lt;/arch&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s how my Interconnect is set up on my local OBS instance (note the project name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openSUSE.org&lt;/code&gt;) -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/obs-opensuse-interconnect.png&quot; alt=&quot;openSUSE.org interconnect&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Hope this helps you!&lt;/p&gt;
</description>
          <pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/obs-rhel-extra/</link>
          <guid isPermaLink="true">https://srivatsp.com/obs-rhel-extra/</guid>
        </item>
    
  
    
	<item>
          <title>veth on MacOS</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Linux virtual-Ethernet (veth) interfaces are quite useful for a variety of purposes - for me, that’s to test &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I recently discovered MacOS also supports something similar - though they are called Fake Ethernet (feth) there.&lt;/p&gt;

&lt;p&gt;Not much information is available on these interfaces and their capabilities, but they do seem to work like basic veth pairs on Linux.&lt;/p&gt;

&lt;p&gt;Here’s how you can create veth-like or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feth&lt;/code&gt; interfaces on MacOS.&lt;/p&gt;

&lt;p&gt;Run the following commands &lt;strong&gt;using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt;&lt;/strong&gt; -&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ifconfig feth0 create
ifconfig feth1 create
ifconfig feth0 peer feth1
ifconfig feth0 up
ifconfig feth1 up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;p&gt;If you send traffic on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feth0&lt;/code&gt; you will receive it on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feth1&lt;/code&gt; and vice-versa.&lt;/p&gt;

&lt;p&gt;If there’s more information about these feth interfaces, do let me know in the comments.&lt;/p&gt;
</description>
          <pubDate>Wed, 10 Dec 2025 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/macos-feth/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/macos-feth/</guid>
        </item>
    
  
    
	<item>
          <title>Saving Ostinato port config</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Back in Feb, as I was telling yet another customer that the easy way to start with &lt;a href=&quot;https://www.notion.so/Ostinato-2-0-d377a107af784607a02db524494792a3?pvs=21&quot;&gt;Ostinato Python API&lt;/a&gt; is not to write python script from scratch, but to save your streams as a python script from the GUI, I remembered that python code is generated only for streams, not for emulated devices or port settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High time I fixed that.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;file-format-infra&quot;&gt;File Format infra&lt;/h2&gt;

&lt;p&gt;The infra is already there. I just need to do some code generation for port settings (Tx mode, port reservation, stream stats etc.) and device groups.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Easy Peasy.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As I started changing the code, I figured out that the infra has access only to the streams, not to port settings or device group config!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Of course, it’s a &lt;em&gt;Save Streams As&lt;/em&gt; functionality. &lt;em&gt;Duh!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We do export everything when you save a session, but that is across all ports of all port groups … overkill for what I wanted – an easy way to get customers to start with the Ostinato Python API.&lt;/p&gt;

&lt;p&gt;What was needed was to implement a &lt;strong&gt;&lt;em&gt;Save As&lt;/em&gt; for port configuration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Like any lazy (&lt;em&gt;read good&lt;/em&gt;) programmer, I created copies of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;streamfileformat.*&lt;/code&gt; files into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;portfilefileformat.*&lt;/code&gt; and started making changes.&lt;/p&gt;

&lt;p&gt;But soon my conscience kicked in.&lt;/p&gt;

&lt;p&gt;We already have infra code duplication between session and streams for open/save. Port open/save was adding to this duplication!&lt;/p&gt;

&lt;h2 id=&quot;its-refactoring-time&quot;&gt;It’s Refactoring time!&lt;/h2&gt;

&lt;p&gt;I refactored &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StreamFileFormat&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SessionFileFormat&lt;/code&gt; classes to use a newly created template class. Some manual testing later, I was confident this was working (&lt;em&gt;and hopefully not causing a regression&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Post the refactoring, I created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PortFileFormat&lt;/code&gt; abstract and concrete classes and the infra for saving port configuration as a &lt;em&gt;native file format&lt;/em&gt; extendable to &lt;em&gt;save as Python script (which is a different save format).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The code was done - but there was no way to invoke it without adding menu items to the GUI.&lt;/p&gt;

&lt;h2 id=&quot;frontend&quot;&gt;Frontend&lt;/h2&gt;

&lt;p&gt;So, I did that next. Plus, the main code to aggregate port settings, all the streams and device groups into a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PortContent&lt;/code&gt; structure that will be passed to the file format infra which will in turn write to file.&lt;/p&gt;

&lt;p&gt;With all that code done, it was time to test stuff out. It was already late Friday evening, but I couldn’t resist the temptation to try it out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💥 CRASH!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that’s where I left it for the weekend.&lt;/p&gt;

&lt;p&gt;Came in Monday morning and fixed the crash and things were working fine!&lt;/p&gt;

&lt;h2 id=&quot;ux-improvement-time&quot;&gt;UX Improvement time&lt;/h2&gt;

&lt;p&gt;Time for some UX enhancements related to this feature.&lt;/p&gt;

&lt;p&gt;First, warn the user about overwriting existing configuration when opening a port file.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/open-port-file-warning.png&quot; alt=&quot;Open Port File warning&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next up - add code to enable drag-drop of Ostinato port files (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.oprt&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/drag-drop-port-file.gif&quot; alt=&quot;Drag and drop to open a port file&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s add some icons to the Port open/save.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/icons-open-save-port-profile.png&quot; alt=&quot;Icons for Open/save port profile menu actions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For good measure I added some icons to the existing Open/Save of Session and Streams which till now didn’t have any icons&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/open-save-icons.gif&quot; alt=&quot;Icons for existing Open/Save session and Stream actions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, I extracted the port related actions into a new top-level menu.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/new-ports-menu.png&quot; alt=&quot;New &amp;quot;Ports&amp;quot; menu in the main menu bar&quot; /&gt;&lt;/p&gt;

&lt;p&gt;and added a prompt/warning when saving streams.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/warning-saving-streams.png&quot; alt=&quot;Warning when saving streams&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;consistent-terminology&quot;&gt;Consistent Terminology&lt;/h2&gt;

&lt;p&gt;After these changes, the phrase &lt;em&gt;Port Configuration&lt;/em&gt; now includes streams and device groups also. So renamed a few things in the UI - the erstwhile &lt;em&gt;Port Configuration&lt;/em&gt; is now &lt;em&gt;Port Settings&lt;/em&gt; to avoid confusion.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/port-config-menu-now-settings.png&quot; alt=&quot;Port Configuration menu action is now Port Settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/port-settings-dialog.png&quot; alt=&quot;Port settings dialog also updated to use Settings instead of Configuration&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Post the renaming in the software, updated the user facing documentation as well.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/user-guide-terminology-update.png&quot; alt=&quot;User Guide updated to use consistent Port Settings and Port Configuration terminology&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;save-port-config-as-python&quot;&gt;Save port config as Python&lt;/h2&gt;

&lt;p&gt;Finally, with the save port config infra now available, migrated the &lt;em&gt;Save streams as python script&lt;/em&gt; to &lt;em&gt;Save port config as python script&lt;/em&gt; by adding port settings and device groups to the code generation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/save-port-as-python.png&quot; alt=&quot;Save port as Python now generates code for port settings and device groups too&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;documentation&quot;&gt;Documentation&lt;/h2&gt;

&lt;p&gt;Added a &lt;a href=&quot;https://userguide.ostinato.org/port-save-open/&quot;&gt;port save/open page&lt;/a&gt; to the user guide.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/user-guide-updates.jpg&quot; alt=&quot;User guide updates for Open/Save Port configuration&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And updated the API guide to use &lt;em&gt;Save port as Python&lt;/em&gt; option instead of &lt;em&gt;Save streams as Python.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/api-guide-recommendation.png&quot; alt=&quot;API guide now recommends using Save Port Configuration as Python script&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Updated internal developer documentation for future reference&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/save-port-cfg/internal-design-documentation.jpg&quot; alt=&quot;Internal design documentation for the file format infra&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And with that, it’s a wrap for this feature!&lt;/p&gt;
</description>
          <pubDate>Tue, 22 Apr 2025 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-save-port/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-save-port/</guid>
        </item>
    
  
    
	<item>
          <title>C++ Enum Support in LuaBridge2</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Recently I was doing some work to add support for Lua scripting to the &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato network traffic generator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Although there are several solutions for Lua bindings for C++ out there, I decided to go with &lt;a href=&quot;https://github.com/vinniefalco/LuaBridge&quot;&gt;LuaBridge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that there is a fork of LuaBridge called &lt;a href=&quot;https://github.com/kunitoki/LuaBridge3&quot;&gt;LuaBridge3&lt;/a&gt;, but it requires C++17 and with Ostinato I target a wide variety of platforms some of which are old and still use C++11.&lt;/p&gt;

&lt;p&gt;So I decided to stick with the original LuaBridge.&lt;/p&gt;

&lt;p&gt;LuaBridge however doesn’t support binding C++ enums. A little bit of googling and reading the documentation I found a solution which I will document here with examples.&lt;/p&gt;

&lt;p&gt;First we will add support for passing C++ enums into Lua (as a number). Given the following C++ enums -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProtocolIdType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ProtocolIdTypeLlc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ProtocolIdTypeEthernet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    
    &lt;span class=&quot;n&quot;&gt;ProtocolIdTypeIp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ProtocolIdTypeTcpUdp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CksumType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;    
    &lt;span class=&quot;n&quot;&gt;CksumTypeIp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CksumTypeIpPseudo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CksumTypeTcpUdp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We add support for these enums by specializing the LuaBridge &lt;a href=&quot;https://vinniefalco.github.io/LuaBridge/Manual.html#s2.7&quot;&gt;Stack template class&lt;/a&gt; for each enum type we want to support.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Helper template for all enums we want to support&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EnumWrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enable_if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lua_State&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lua_pushnumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enable_if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lua_State&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lua_tointeger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Use EnumWrapper template for each enum type we want to support&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;luabridge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Stack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProtocolIdType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EnumWrapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProtocolIdType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Stack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CksumType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EnumWrapper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CksumType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you can pass the enums back and forth between C++ and Lua - the C++ enums will be treated as integers in Lua. So, you can call a Lua function pass in a C++ enum value.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;LuaRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payloadProtocolId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getGlobal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;payloadProtocolId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payloadProtocolId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProtocolIdTypeIp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;LuaRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getGlobal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;payloadProtocolIdType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ProtocolIdType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;protocolId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remember in Lua code these variables are integers. So any code referencing these will need to compare against the integer values.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payloadProtocolIdType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;-- do something&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What if you wanted to use enums instead of integers in Lua? You can export them as Lua global variables by registering them with LuaBridge as a property under a Lua namespace (table).&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Helper template function for registering enum constants with LuaBridge&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enumValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;getGlobalNamespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beginNamespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Protocol&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Enum ProtocolIdType&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProtocolIdLlc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProtocolIdLlc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProtocolIdEth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProtocolIdEth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProtocolIdIp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProtocolIdIp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProtocolIdTcpUdp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProtocolIdTcpUdp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Enum CksumType&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CksumIp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CksumIp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CksumIpPseudo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CksumIpPseudo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CksumTcpUdp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CksumTcpUdp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endNamespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After this registration, you can now reference the enums in Lua code as global variables.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;protocolId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProtocolIdIp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it. Hope this has been helpful.&lt;/p&gt;
</description>
          <pubDate>Wed, 19 Feb 2025 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/luabridge-enum/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/luabridge-enum/</guid>
        </item>
    
  
    
	<item>
          <title>QtScript to QML Migration - Notes from the Trenches</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Ostinato uses the QtScript module to support &lt;a href=&quot;https://userguide.ostinato.org/user-script/&quot;&gt;custom protocol userscripts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Qt5 had deprecated the QtScript module and replaced it with a new Javascript engine in the QML module. With Qt6, the QtScript module has been removed.&lt;/p&gt;

&lt;p&gt;To migrate Ostinato from Qt5 to Qt6, I needed to figure out the migration changes for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptEngine&lt;/code&gt; and related classes which were part of QtScript.&lt;/p&gt;

&lt;p&gt;A search for a migration guide from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptEngine&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSEngine&lt;/code&gt; didn’t return any useful results - &lt;em&gt;which is very unlike Qt documentation!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the end, the changes needed turned out to be straightforward. YMMV but I’m sharing my notes here - this should get you started in the right direction.&lt;/p&gt;

&lt;h2 id=&quot;qmakecmake-changes&quot;&gt;Qmake/CMake changes&lt;/h2&gt;
&lt;p&gt;To start, I suggest you make changes to your qmake/cmake files to replace QtScript module with QML module. Make this change first so that you can trigger builds to identify compilation issues after every change.&lt;/p&gt;

&lt;p&gt;In your qmake .pro file(s), replace&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;- QT += script
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ QT += qml
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If using CMake, replace&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;- find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Script)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Qml)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;qscriptengine--qjsengine&quot;&gt;QScriptEngine ⇒ QJSEngine&lt;/h2&gt;
&lt;p&gt;The main scripting engine class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptEngine&lt;/code&gt; has been replaced with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSEngine&lt;/code&gt;. So do a find-replace for that along with the header file change -&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;-#include &amp;lt;QScriptEngine&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+#include &amp;lt;QJSEngine&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptEngine::hasUncaughtException()&lt;/code&gt; you should use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSEngine::evaluate()&lt;/code&gt; and check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isError()&lt;/code&gt; of the returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSValue&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;-    engine_.evaluate(script);
-    if (engine_.hasUncaughtException())
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    QJSValue value = engine_.evaluate(script);
+    if (value.isError())
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;qscriptvalue--qjsvalue&quot;&gt;QScriptValue ⇒ QJSValue&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue&lt;/code&gt; equivalent is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSValue&lt;/code&gt;. But some method names differ between the two. e.g.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue::isValid()&lt;/code&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!QJSValue::isUndefined()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue::isFunction()&lt;/code&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSValue::isCallable()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue::call()&lt;/code&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSValue::callWithInstance()&lt;/code&gt; (for object method calls)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue::toUInt32()&lt;/code&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSValue::toUInt()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue::toInt32()&lt;/code&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSValue::toInt()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can do a find-replace for the above or redefine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue&lt;/code&gt; to inherit from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSValue&lt;/code&gt; and override the methods as shown below.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;QScriptValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QJSValue&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isUndefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isCallable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toUInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toUInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;    
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue&lt;/code&gt; redefinition to start with to quickly get things compiling and working. Once compilation was successful and my tests were passing, I did find-replace for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QScriptValue&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QJSValue&lt;/code&gt; alongwith the changed methods and removed the redefinition.&lt;/p&gt;

&lt;p&gt;These were the only changes I needed to make. You may find more changes are needed but hopefully this will get you a good way there.&lt;/p&gt;

&lt;h2 id=&quot;gotchas&quot;&gt;Gotchas&lt;/h2&gt;
&lt;p&gt;There are a couple of gotchas though e.g. A C++ QObject’s dynamic properties are not accessible in the script which has access to the object. However, properties added to the object in the script are accessible to the C++ code.&lt;/p&gt;
</description>
          <pubDate>Sat, 18 Jan 2025 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/qtscript-migration/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/qtscript-migration/</guid>
        </item>
    
  
    
	<item>
          <title>doori</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;iframe class=&quot;center&quot; src=&quot;https://www.youtube.com/embed/xr8u12TjKsE?rel=0&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;the-backstory&quot;&gt;The backstory&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Watch the video first!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The other day while I was browsing my LinkedIn feed, it stuck me suddenly that the logo for &lt;a href=&quot;http://dori.ai&quot;&gt;dori.ai&lt;/a&gt; is very similar to the Doordarshan (DD) logo.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/doori/dori-linkedin-update.png&quot; alt=&quot;Dori LinkedIn update&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was about to comment on this similarity on a WhatsApp group of friends and former colleagues.&lt;/p&gt;

&lt;p&gt;Instead, I spent an hour making this video! 🤷🏽&lt;/p&gt;

&lt;p&gt;The title &lt;em&gt;doori&lt;/em&gt; is of course a portmanteau of Dori and Doordarshan.&lt;/p&gt;

&lt;p&gt;A friend who works at Dori assures me that the logo designer likely never heard of DD or even encountered the DD logo anywhere.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/doori/no-copy.png&quot; alt=&quot;No copy&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;about-dori&quot;&gt;About Dori&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/doori/dori-logo.png&quot; alt=&quot;Dori.ai logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://dori.ai&quot;&gt;dori.ai&lt;/a&gt; is a company founded by former colleagues of mine, providing AI-enabled computer-vision solutions for manufacturing, logistics and other enterprise use-cases.&lt;/p&gt;

&lt;h3 id=&quot;about-doordarshan&quot;&gt;About Doordarshan&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/doori/dd-logo.png&quot; alt=&quot;Doordarshan logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Doordarshan&quot;&gt;Doordarshan&lt;/a&gt; (DD) is India’s National Channel (aka public broadcaster) and the first ever broadcast TV channel launched in 1959 in India. My generation grew up watching the once-a-week movie broadcast on Sundays at a neighbor’s (who had a TV, very few households did) along with other kids from the street.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.youtube.com/watch?v=iPjXxH7tJpo&quot;&gt;montage of the DD logo with the signature tune&lt;/a&gt; composed by Pt. Ravi Shankar was broadcast every day when it began transmission for the day (&lt;em&gt;‘twas not the days of 24-hour channels!&lt;/em&gt;) is iconic and instantly recognizable for everyone who grew up with it.&lt;/p&gt;

&lt;h3 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h3&gt;

&lt;p&gt;This video mashup is a creative expression made solely for entertainment purposes.&lt;/p&gt;

&lt;p&gt;This mashup is independently created and is not affiliated with, endorsed by, or connected to &lt;em&gt;Dori.ai&lt;/em&gt;, &lt;em&gt;Doordarshan&lt;/em&gt;, or any of their associated entities. All trademarks, logos, and media used remain the property of their respective owners.&lt;/p&gt;
</description>
          <pubDate>Tue, 29 Oct 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/doori/</link>
          <guid isPermaLink="true">https://srivatsp.com/doori/</guid>
        </item>
    
  
    
	<item>
          <title>PCAP import - a crash and a flash</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A few months ago an &lt;a href=&quot;https://ostinato.org/&quot;&gt;Ostinato&lt;/a&gt; customer reported the Ostinato GUI crashing when importing a large PCAP file (~600MB).&lt;/p&gt;

&lt;p&gt;After a few failed attempts at reproducing the error on Linux, I realized the customer was using Windows and switched to that.&lt;/p&gt;

&lt;p&gt;I was able to reproduce the crash with Ostinato on Windows - both with intelligent import enabled or disabled and set out to debug the same.&lt;/p&gt;

&lt;p&gt;I started with intelligent import disabled since it would be easier.&lt;/p&gt;

&lt;video width=&quot;100%&quot; controls=&quot;&quot; playsinline=&quot;&quot;&gt;
  &lt;source src=&quot;https://srivatsp.com/assets/images/ost-pcap-import/big-pcap-crash.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;p&gt;No error of any kind. The GUI just closes and the process doesn’t exist. There was no obvious error.&lt;/p&gt;

&lt;p&gt;To make matters worse, the crash happened at 99% and it took almost 50min to reach this point. It’s going to be a pain to debug if every repro takes an hour!&lt;/p&gt;

&lt;p&gt;So the first order of business was to code up a hack to reduce the import time - while still being able to reproduce the crash. And that’s just what I did.&lt;/p&gt;

&lt;p&gt;Phew! At least now I can run iterations without having to wait for an hour each time! Back to debugging. gdb just reported a SEGV and program terminated. I couldn’t even determine where in the code it was crashing.&lt;/p&gt;

&lt;p&gt;printf-debugging wouldn’t work because we were importing millions of packets in a loop - &lt;em&gt;not that I didn’t try!&lt;/em&gt; In fact, it took me down a tunnel where I suspected the crash was possibly inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libprotobuf&lt;/code&gt; and so I spent a day or two upgrading that - to no success.&lt;/p&gt;

&lt;h2 id=&quot;out-of-memory&quot;&gt;Out of Memory?&lt;/h2&gt;

&lt;p&gt;I suspected we were running out of memory but there was enough free memory in the system (32GB RAM) and task manager showed a usage of ~1GB. I was really out of ideas after a day or two of debugging and decided to park it for some time and promptly got distracted with other Ostinato bugs and features.&lt;/p&gt;

&lt;p&gt;Until 2 weeks ago when another customer reported the same issue. And so I got back to it once again - but still no luck!&lt;/p&gt;

&lt;p&gt;Till I had the brainwave of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;catch throw&lt;/code&gt; in gdb!&lt;/p&gt;

&lt;p&gt;It did turn out to be a memory issue as suspected - an exception was being thrown from the C++ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt; memory allocator. &lt;em&gt;But I still didn’t get it&lt;/em&gt; - there’s a lot of memory free in the system and the process is using only about 1GB as per task manager!&lt;/p&gt;

&lt;p&gt;A real head-scratcher.&lt;/p&gt;

&lt;h2 id=&quot;virtual-memory-address-space&quot;&gt;Virtual memory address space?&lt;/h2&gt;

&lt;p&gt;More googling and asking ChatGPT I got another hint - check the virtual address space of the process! A win32 binary has a 4GB virtual space of which only 2GB is available to the user space. The memory use reported by task manager was titled “Working set (memory)” which as per task manager itself means “Amount of physical memory currently in use by the process”.&lt;/p&gt;

&lt;p&gt;So I ran &lt;a href=&quot;https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer&quot;&gt;procexp (process explorer)&lt;/a&gt; and to my good luck it reported the virtual address space usage by the process! So I ran the PCAP import again and watched the virtual address space of the process slowly increasing from the base 1GB as we imported more packets &lt;strong&gt;till it crashed and procexp showed a virtual space usage of ~2GB&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-pcap-import/drone-2GB.png&quot; alt=&quot;Ostinato crashes when Virtual Size reaches 2GB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This also explains why I wasn’t able to repro on Linux - because my Linux dev env is 64-bit while my Windows Ostinato dev env is 32-bit!.&lt;/p&gt;

&lt;p&gt;My Windows 10 itself is 64-bit but my Qt/Mingw chain is 32-bit due to historical reasons. There was never an incentive earlier to upgrade it to 64-bit. Setting up a new development environment with all the dependencies would take a couple of days (or more!) which is why I’ve never convinced myself to do it before now!&lt;/p&gt;

&lt;p&gt;When I explained the problem to the customer, he was happy to switch to Linux to make progress (&lt;em&gt;and he did!&lt;/em&gt;). So I bought myself some breathing time. And I decided to do some more experiments to better understand the problem and handle it gracefully.&lt;/p&gt;

&lt;h2 id=&quot;try-and-recover&quot;&gt;Try and Recover&lt;/h2&gt;

&lt;p&gt;Unfortunately all my attempts at using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;try…catch&lt;/code&gt; block failed to catch the exception and went straight to SEGV (segmentation fault).&lt;/p&gt;

&lt;p&gt;I wasn’t sure what was happening. But one thing that I did see was that the exception can happen in any thread, not just the one that is doing the pcap import and consumes bulk of the memory. This is because the app and QT in other threads could be doing their own heap allocations which could trigger the same exception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So unless I can try…catch all the threads, it’s not of any use!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which brings us to the question - even if we wrapped &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;try...catch&lt;/code&gt; around all threads - what do we gain?&lt;/p&gt;

&lt;p&gt;To be able to identify the problem and handle it gracefully, try…catch will have to be granular, not at the very top of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QThread::run()&lt;/code&gt; - the latter has no hope of recovery.&lt;/p&gt;

&lt;p&gt;The only way to handle this seemed to be to monitor memory use ourselves in the thread during pcap import and avoid the crash altogether by aborting the import if we reach say 90% of available memory to the process.&lt;/p&gt;

&lt;p&gt;Given that there is no portable cross-platform way to find out available free memory for a process, this is a fools errand for none to little gain. Which leads me to the following conclusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We’ll just upgrade Ostinato for Windows from 32-bit to 64-bit going forward!&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;speed-from-hack-to-fix&quot;&gt;Speed: From Hack to Fix&lt;/h2&gt;

&lt;p&gt;But it wasn’t all in vain.&lt;/p&gt;

&lt;p&gt;The hack I wrote up to reduce the PCAP import time - I cleaned it up and converted it into a proper optimization to reduce the PCAP import time. A 500MB PCAP file with 450,000 packets of average size 1218 bytes is now imported in a flash!&lt;/p&gt;

&lt;iframe class=&quot;center fitvidsignore&quot; width=&quot;480&quot; height=&quot;480&quot; src=&quot;https://www.youtube.com/embed/lMus06sRnu4?rel=0&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Note this is only for raw PCAP import (i.e. when you uncheck the intelligent import option - which is by default checked when you open a PCAP file in Ostinato). With raw pcap import, packets are just read in as hex dumps into Ostinato.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://userguide.ostinato.org/images/pcapImportOptions.png&quot; alt=&quot;https://userguide.ostinato.org/images/pcapImportOptions.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also reduced the time taken to delete existing streams (when importing a PCAP file in &lt;em&gt;overwrite&lt;/em&gt; mode) and in the process direct delete of Ostinato streams in the GUI should also be MUCH faster now!&lt;/p&gt;

&lt;p&gt;For intelligent PCAP import (aka via PDML) I intend to adopt a different strategy of writing a multi-core standalone converter program for the command-line that can be used by the Ostinato Python API as well.&lt;/p&gt;

&lt;p&gt;Stay tuned for that!&lt;/p&gt;

&lt;p&gt;Do you have any pet peeves while using Ostinato? Let me know in the comments below!&lt;/p&gt;
</description>
          <pubDate>Tue, 15 Oct 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/pcap-import-crash-flash/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/pcap-import-crash-flash/</guid>
        </item>
    
  
    
	<item>
          <title>Making Ostinato&apos;s variable fields more flexible</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Back in 2020, &lt;em&gt;Javor&lt;/em&gt; wanted to test 2 million CGNAT scale using &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; but was running into a problem generating the flows he wanted.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;The stream just generated sequentially flows one by one like this for example:&lt;/em&gt;&lt;br /&gt;
src-ip_1, src port 30001 -&amp;gt; dst-ip_1, port 1001&lt;br /&gt;
src-ip_2, src port 30002 -&amp;gt; dst-ip_2, port 1002&lt;br /&gt;
src-ip_3, src port 30003 -&amp;gt; dst-ip_3, port 1003&lt;br /&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;But I don’t want this. I would like to achieve the following way of order and incrementation:&lt;/em&gt;&lt;br /&gt;
src_ip1, src port 30001 -&amp;gt;  dst-ip_1, port 1001&lt;br /&gt;
src_ip1, src port 30002 -&amp;gt;  dst-ip_1, port 1002&lt;br /&gt;
src_ip1, src port 30003  -&amp;gt; dst-ip_1, port 1003&lt;br /&gt;
src_ip1, src port 30004  -&amp;gt; dst-ip_1, port 1004&lt;br /&gt;
src_ip1, src port 30005  -&amp;gt; dst-ip_1, port 1005&lt;br /&gt;
……&lt;br /&gt;
&lt;em&gt;So I would like the stream to generate the packets in order to start first from src-ip and incrementally passthrough over all src-ports range ( for example range of 5000 ports ) and then to go to the next src-ip then to next src-ip and so on….&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Irrespective of how many variable fields you have in a Ostinato stream, each variable field increments with every packet - which was not what Javor wanted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;He wanted a counter behavior similar to nested loops.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I filed issue #302 on the Ostinato issue tracker to keep track of the feature request.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/issue.png&quot; alt=&quot;Ostinato variable fields enhancement feature request&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-workarounds&quot;&gt;The Workaround(s)&lt;/h2&gt;

&lt;p&gt;For Javor’s use case I came up with a workaround to do what he wanted using the variable field mask and &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-nat-variable-fields/&quot;&gt;wrote a blog post&lt;/a&gt; about it.&lt;/p&gt;

&lt;p&gt;But the variable field mask workaround had a limitation of being able to use only those counts which are power of 2 (&lt;em&gt;read the blog post for details&lt;/em&gt;). In addition to this limitation, the solution is not very intuitive for users to understand and use.&lt;/p&gt;

&lt;p&gt;Last week another customer &lt;em&gt;Ashok&lt;/em&gt; had a similar requirement to test Linux conntrack with scaled sessions. However, Ashok’s scale was lower and he was ok with creating &lt;a href=&quot;https://userguide.ostinato.org/streams/&quot;&gt;multiple Ostinato streams&lt;/a&gt; - one each for a pair of source and destination UDP ports while each Ostinato stream would vary IP addresses 4000 times. 20 streams of 4000 IP address each makes for 80K flows.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/multiple-streams.png&quot; alt=&quot;Multiple Ostinato streams as a workaround for lack of nested variable fields&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;/h2&gt;

&lt;p&gt;I figured it was time to dust off this issue and look into what it takes to implement it properly. I worked it out an example on paper first -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/paper-working.jpg&quot; alt=&quot;Working out variable fields behavior on paper first&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now that I knew what needed to be done, I did a quick and dirty test with a hard-coded value -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/hardcoded-prototype.png&quot; alt=&quot;Quick and dirty feature test with hardcoded value&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And it worked!&lt;/strong&gt; &lt;br /&gt;
&lt;strong&gt;Yup, that one-line change was the essential core of what was required!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, I needed to replace that hard coded value with a user-specified parameter.&lt;/p&gt;

&lt;p&gt;I spent a fair amount of time trying to come up with a suitable name for the new parameter. Struggling to come up with a name I liked, I asked ChatGPT to suggest some names.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/name-suggestions.png&quot; alt=&quot;Ask ChatGPT for suggestions for parameter name&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I short listed a few and sent a message to a few friends to solicit human feedback!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/user-research.png&quot; alt=&quot;Solicit user feedback&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Based on the feedback received and my own thoughts, I decided to finalize on calling the parameter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skip&lt;/code&gt; - &lt;em&gt;I wasn’t entirely convinced of the name as it may lead to off-by-one confusion, but decided to go ahead with it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Other code changes were just to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skip&lt;/code&gt; field to the UI and the Protobuf RPC.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/new-gui-param.png&quot; alt=&quot;Adding the new &apos;skip&apos; param to the GUI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the end, it turned out to be actually a very small code change that need not have been on the issue tracker for 4 years!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/commit.png&quot; alt=&quot;Git feature commit&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;note-to-self&quot;&gt;Note to Self&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;I am more motivated to implement or fix something when there’s a related trigger - in this case the new customer wanting this same feature. In the future I will take advantage of this increased motivation to at least do a prototype/scoping of the feature right away.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;Finally, to wrap up the feature, I added a test to the variable fields test suite and updated the &lt;a href=&quot;https://userguide.ostinato.org/stream-config/#variable-fields&quot;&gt;Ostinato User Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/vary-every/test-case.png&quot; alt=&quot;New variable fields automated test case&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Is there a feature you’d like added to Ostinato? Let me know in the comments!&lt;/p&gt;
</description>
          <pubDate>Tue, 10 Sep 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/making-ostinato-variable-fields-flexible/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/making-ostinato-variable-fields-flexible/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato Timed Transmit</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Back in May, I implemented &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-transmit-duration&quot;&gt;Transmit Duration&lt;/a&gt; in Ostinato. This was a pre-requisite to the next feature I planned to implement - &lt;em&gt;Timed Transmit&lt;/em&gt;. I even teased a GUI image fragment.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-transmit-duration/ostinato-timed-tx-teaser.png&quot; alt=&quot;Timed Transmit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, 2 months passed before I could start working on Timed Transmit (&lt;em&gt;Solopreneur, juggling multiple hats!&lt;/em&gt;)!&lt;/p&gt;

&lt;h2 id=&quot;whats-timed-transmit&quot;&gt;What’s Timed Transmit?&lt;/h2&gt;
&lt;p&gt;The user specifies a time duration for Transmit and the agent automatically stops the transmit after that time - &lt;em&gt;simple!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you are a techie, you’ve by now subconsciously translated the above feature definition to&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Add a input field in the GUI, start a timer when starting transmit and on timeout stop the transmit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And you’d be right - that’s what I thought too!&lt;/p&gt;

&lt;h2 id=&quot;the-implementation&quot;&gt;The implementation&lt;/h2&gt;
&lt;p&gt;It ended up taking a full week to implement - as it often happens, in my excitement I hadn’t spent the time to break down all that was needed to be done before beginning the work!&lt;/p&gt;

&lt;p&gt;What all was needed, you ask? Here goes -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GUI mock-ups and experiments to finalize the widgets required, look and UX&lt;/li&gt;
  &lt;li&gt;Work that was done and then discarded as I found better solutions e.g. I added a new RPC first instead of changing the RPC signature for backward compatibility reasons; but when later I found a way to maintain backward compatiblity with the RPC change I discarded the new RPC&lt;/li&gt;
  &lt;li&gt;Controller-Agent RPC signature change requiring code changes on both controller and agent side&lt;/li&gt;
  &lt;li&gt;Debugging problems with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTimer&lt;/code&gt; - researching/reading documentation and then working around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTimer&lt;/code&gt; documented behaviour/limitations in multi-threaded applications&lt;/li&gt;
  &lt;li&gt;Handling various edge/error cases (&lt;em&gt;the 80:20 rule!&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;Researching, experimenting on ways to hide the RPC signature change so that existing scripts using the Ostinato Python API do not require changes while still supporting the newer RPC with transmit duration&lt;/li&gt;
  &lt;li&gt;Python API changes for the new RPC signature including figuring out how to deprecate the older &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startTransmit()&lt;/code&gt; RPC so that script users are forewarned about a future removal&lt;/li&gt;
  &lt;li&gt;Add automated RPC tests for both new and old (deprecated) APIs&lt;/li&gt;
  &lt;li&gt;Debugging RPC test failures because I didn’t read the debug logs carefully and went down the wrong tunnel (&lt;em&gt;What a time sink!&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;Python API documentation update&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Save As Python Script&lt;/em&gt; changes in the GUI to use the newer RPC&lt;/li&gt;
  &lt;li&gt;User Guide documentation update&lt;/li&gt;
  &lt;li&gt;Feature video (see below)&lt;/li&gt;
  &lt;li&gt;This blog post! (&lt;em&gt;a previous draft was so long and meandering, that I gave up in the middle of writing it and did a full rewrite&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is as much a list for me to remind myself next time I underestimate a feature!&lt;/p&gt;

&lt;h2 id=&quot;timed-transmit-in-action&quot;&gt;Timed Transmit in action&lt;/h2&gt;

&lt;iframe class=&quot;center fitvidsignore&quot; width=&quot;480&quot; height=&quot;852&quot; src=&quot;https://www.youtube.com/embed/Cmcmjuau8Y8?rel=0&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Hope, you enjoyed that!&lt;/p&gt;

&lt;p&gt;What do you think of this new feature? Let me know in the comments below!&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Wed, 31 Jul 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-timed-transmit/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-timed-transmit/</guid>
        </item>
    
  
    
	<item>
          <title>A Qt app with auto-resizing docks</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Most desktop apps have a main window with one child window which displays the &lt;em&gt;document&lt;/em&gt; surrounded by toolbars, toolboxes and palettes. You know what I’m talking about.&lt;/p&gt;

&lt;p&gt;Here’s an image from the Qt documentation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/qt-dock-auto-resize/qt-main-window-layout.png&quot; alt=&quot;Qt app main window layout&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; (a desktop app built using Qt), however, has a main window with 3 dock windows (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QDockWidget&lt;/code&gt;) - &lt;em&gt;Ports &amp;amp; Streams&lt;/em&gt;, &lt;em&gt;Port Statistics&lt;/em&gt; and &lt;em&gt;Logs&lt;/em&gt;, but no central widget.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/qt-dock-auto-resize/ostinato-startup-main-window-layout.png&quot; alt=&quot;Ostinato main window at start&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When Ostinato starts, two child windows are programmatically set to equally share the vertical space. The third &lt;em&gt;Logs&lt;/em&gt; window is added as a tab to the &lt;em&gt;Port Statistics&lt;/em&gt; window, since it is informational and need not occupy screen estate, until required.&lt;/p&gt;

&lt;p&gt;Now as long as you don’t change the size of the main window all is good. But if you increase (or maximize) the window, you end up with something like this -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/qt-dock-auto-resize/ostinato-maximized-main-window-layout.png&quot; alt=&quot;Ostinato main window maximized&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ugh!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All that wasted space and ugly looking to boot!&lt;/p&gt;

&lt;p&gt;I immediately drag the two windows so that they take up the full space again, before continuing to work.&lt;/p&gt;

&lt;p&gt;But some customers don’t. Or worse - &lt;strong&gt;don’t realize that they can resize the windows manually&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;This wasn’t the highest priority item to address all these years and so I didn’t pay much attention to it other than cursory look for some solution, which I didn’t find.&lt;/p&gt;

&lt;p&gt;Solution to what you ask?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To utilize all the space available for the Ostinato dock windows whenever the main window is resized.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Last week, I decided to take a look (&lt;em&gt;read: I was tired after some serious debugging of a customer issue and needed something lighter to tackle!&lt;/em&gt;).&lt;/p&gt;

&lt;h2 id=&quot;first-try&quot;&gt;First try&lt;/h2&gt;
&lt;p&gt;My first thought was to hook into main window resizes using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QMainWindow::resizeEvent()&lt;/code&gt; and programmatically resize the dock windows using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QMainWindow::resizeDocks()&lt;/code&gt; to take up half the main window each as done at start up.&lt;/p&gt;

&lt;p&gt;Which worked.&lt;/p&gt;

&lt;p&gt;But, I immediately realized that before the user changes the main window size, they might have changed the layout or sizes of the dock windows. In such a situation, &lt;strong&gt;any change to the main window size should retain the user layout&lt;/strong&gt; and only change the dock window sizes proportionally.&lt;/p&gt;

&lt;h2 id=&quot;second-try&quot;&gt;Second try&lt;/h2&gt;
&lt;p&gt;My second attempt was to try and calculate the new sizes of the dock windows as follows -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dockNewSize = (newMainWindowSize/oldMainWindowSize)*dockOldSize
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which is all fine and dandy. Within &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QMainWindow::resizeEvent()&lt;/code&gt; the input parameter gives us both the old and new size of the main window, but the dock windows have already been resized by Qt’s own logic and we don’t have access to the old dock sizes!&lt;/p&gt;

&lt;p&gt;The only way to get access is to maintain the dock sizes across all dock and main windows resizes. That’s &lt;strong&gt;too much trouble for a lazy programmer&lt;/strong&gt; like me!&lt;/p&gt;

&lt;p&gt;That’s when I thought of a different way to approach the problem.&lt;/p&gt;

&lt;h2 id=&quot;third-try&quot;&gt;Third try&lt;/h2&gt;
&lt;p&gt;Don’t change the dock sizes at all. Instead, change the central widget’s size.&lt;/p&gt;

&lt;p&gt;Of course, we didn’t have a central widget currently, so I created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dummyCentral&lt;/code&gt; (a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QWidget&lt;/code&gt;) and called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QMainWindow::setCentralWidget(dummyCentral)&lt;/code&gt;. Then, in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resizeEvent()&lt;/code&gt;, call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dummyCentral-&amp;gt;resize(0, 0)&lt;/code&gt;. Setting the central widget to a size 0 would make the dock windows automatically resize utilizing all the available space while retaining the current dock layout!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pure Genius!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Except that it didn’t work.&lt;/p&gt;

&lt;p&gt;(&lt;em&gt;It should have, but it didn’t. Continue reading to know why!&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;It was late and I decided to call it a day feeling mentally exhausted.&lt;/p&gt;

&lt;p&gt;But as so often happens when you go to bed with a problem on your mind, you don’t sleep well and you are tossing and turning with thoughts of the problem still in your head.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“&lt;em&gt;The central widget resizing strategy &lt;strong&gt;should&lt;/strong&gt; work - there’s no reason why it shouldn’t!&lt;/em&gt;”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I decided to take another look the next day.&lt;/p&gt;

&lt;h2 id=&quot;fourth-time-is-the-charm&quot;&gt;Fourth time is the charm&lt;/h2&gt;
&lt;p&gt;While looking at the code for the (dummy) central widget that I had written, I realized I didn’t need to resize the dummy widget, if I set it to (0, 0) &lt;strong&gt;before&lt;/strong&gt; I originally add it to the main window!&lt;/p&gt;

&lt;p&gt;Unless, Qt handles (0, 0) size specially and doesn’t add it to the main window at all (&lt;em&gt;No, it doesn’t handle it specially&lt;/em&gt;)!&lt;/p&gt;

&lt;p&gt;Also, it helps if you can see something on the screen while debugging!&lt;/p&gt;

&lt;p&gt;So, I removed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resizeEvent()&lt;/code&gt; method and created a QLabel as the central widget and set its text to XANADU.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t ask me why - it was the first word that came to mind; something to do with Mandrake, the magician I presume!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I ran the code, &lt;strong&gt;no XANADU on the screen&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Head-scratcher! But something to dig further!&lt;/p&gt;

&lt;p&gt;Looking at the dummy widget creation code and the code around it, I noticed that I was calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setCentralWiget()&lt;/code&gt; before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setupUi()&lt;/code&gt; (the main window uses a Qt .ui file).&lt;/p&gt;

&lt;p&gt;That didn’t seem right, so I moved the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setCentralWidget()&lt;/code&gt; after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setupUi()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addDockWidget()&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voila!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;XANADU magically appears inside the main window!&lt;/p&gt;

&lt;p&gt;Maximizing the main window resizes the docks to utilize all the space around XANADU!&lt;/p&gt;

&lt;p&gt;Here’s my final cleaned-up code that I committed to the repo.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;QWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setFixedSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// fixed =&amp;gt; both min and max size&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setStyleSheet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;background: red&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Set central widget after adding docks&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;setCentralWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s the result of adding those 4 lines of code - docks use all the space available!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/qt-dock-auto-resize/ostinato-docks-use-full-space.gif&quot; alt=&quot;Docks utilizing all the space&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The careful reader would have noted and may be wondering, &lt;strong&gt;why I set the dummy widget height to 10&lt;/strong&gt; instead of 0?&lt;/p&gt;

&lt;p&gt;With a size of (0, 0) for the central widget, the left/right dock areas don’t seem to be available, so using a width of 0 ensures that the widget is not visible and a height of 10px ensures that there is some space between top and bottom docks that user can insert a dock in the left/right areas (to create a middle row, so to speak).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Even with the 10px, I find that it’s still quite fiddly dragging a dock to the left/right dock areas for the main window to recognize and visually indicate its readiness to accept the drop there! Increasing the height of the main window seems to help.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The red background is not really needed, but I left it in for debug purposes - I just need to change the width to a non-zero value and the red widget should show up and stand out on screen.&lt;/p&gt;

&lt;p&gt;Here’s a screenshot with three &lt;del&gt;ducks&lt;/del&gt; docks in a &lt;del&gt;row&lt;/del&gt; column -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/qt-dock-auto-resize/ostinato-three-row-layout.png&quot; alt=&quot;Three docks in a column&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ok, that pun was a stretch, I know!&lt;/p&gt;

&lt;p&gt;But talking of stretches, the three row dock windows are not behaving like I’d like them to with respect to vertical stretch (logs window seems to hog the space, while I would like the stats window to get the space). The usual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QWidget::sizePolicy::verticalStretch&lt;/code&gt; doesn’t work because these three windows are not in a normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QLayout&lt;/code&gt;, but in three separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QDockWidget&lt;/code&gt; laid out by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QMainWindow&lt;/code&gt;’s dock-aware layout manager.&lt;/p&gt;

&lt;p&gt;I probably need to play around with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sizeHint()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sizePolicy()&lt;/code&gt; or the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;minimum()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maximum()&lt;/code&gt; size constraints for one or more of those dock windows to get the vertical space utilization I’d like - but that’s a bunch of experiments for another day (&lt;em&gt;and another blog post&lt;/em&gt;)!&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 18 Jun 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/qt-auto-resizing-docks/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/qt-auto-resizing-docks/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato Show Transmit Duration</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;The Ostinato &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-latency-jitter/&quot;&gt;latency and jitter&lt;/a&gt; feature sends a &lt;em&gt;timing tagged (TTAG) packet&lt;/em&gt; roughly every 5 seconds. While implementing the feature, I often had to keep track of the transmit duration to make sure that enough number of timing packets were sent out for the test case I was trying to verify.&lt;/p&gt;

&lt;p&gt;Manually tracking time was doable, but not ideal - so I added &lt;em&gt;Show running Transmit Duration&lt;/em&gt; as a future feature to work on.&lt;/p&gt;

&lt;p&gt;The latency and jitter shipped as part of &lt;a href=&quot;https://userguide.ostinato.org/changelog/#2023-10-19-version-130&quot;&gt;Ostinato v1.3.0&lt;/a&gt;. Last week I got around to implementing the show transmit duration feature for the next release. It shows not just the running transmit duration, but also the last transmit duration as well.&lt;/p&gt;

&lt;p&gt;Here’s a short video preview of this feature -&lt;/p&gt;

&lt;iframe class=&quot;center fitvidsignore&quot; width=&quot;400&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/_BIJkNRinIw?rel=0&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Hope, you enjoyed that!&lt;/p&gt;

&lt;p&gt;What do you think of this new feature? Let me know in the comments below!&lt;/p&gt;

&lt;p&gt;Next up, timed transmit with auto-stop -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-transmit-duration/ostinato-timed-tx-teaser.png&quot; alt=&quot;Timed Transmit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 28 May 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-transmit-duration/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-transmit-duration/</guid>
        </item>
    
  
    
	<item>
          <title>Marketing to Developers</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;As a product creator with an engineering background, I’m always trying to learn more about good marketing.&lt;/p&gt;

&lt;p&gt;So when I saw this tweet from Kelsey Hightower, I got spelunking into the many many replies to it - from developers themselves!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/kelseyhightower/status/1775913633064894669&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/marketing-to-developers/tweet-marketing-to-developers.png&quot; alt=&quot;Marketing strategies targeting Developers&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although my product &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; is not targeted at developers but network engineers - the latter audience I believe is similar enough for the replies to be useful to me.&lt;/p&gt;

&lt;p&gt;I summarized and categorized the replies so that I can refer to in the future without having to re-read the thread again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/marketing-to-developers/marketing-to-developers.png&quot; alt=&quot;Marketing to Developers&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can also download the above summary as a &lt;a href=&quot;https://srivatsp.com/assets/images/marketing-to-developers/marketing-to-developers.pdf&quot;&gt;handy PDF&lt;/a&gt;.&lt;/p&gt;

</description>
          <pubDate>Mon, 08 Apr 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/marketing-to-developers/</link>
          <guid isPermaLink="true">https://srivatsp.com/marketing-to-developers/</guid>
        </item>
    
  
    
	<item>
          <title>Generate RARP (Reverse ARP) packets</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Recently, &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; customer Barry sent me this email -&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I want to generate RARP request packets. I created an ARP packet in a stream. Saved the stream as a PCAP file. Hex edited the PCAP file to change the type to 0x0835. Imported and tried to send. It is not working.&lt;br /&gt;
Do you have a suggestion?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/rfc903/&quot;&gt;RARP&lt;/a&gt; (Reverse ARP) is a very old protocol that is not used these days and so Ostinato does not support it (&lt;em&gt;Barry  was working with some old equipment that still used RARP&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;I looked up the RARP frame format and found that it is exactly the same as ARP except for two fields.&lt;/p&gt;

&lt;p&gt;Here’s the RARP RFC 903 extract -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   RARP uses the same packet format that is used by ARP, namely:

      ar$hrd (hardware address space) -  16 bits
      ar$pro (protocol address space) -  16 bits
      ar$hln (hardware address length) - 8 bits
      ar$pln (protocol address length) - 8 bits
      ar$op  (opcode) - 16 bits
      ar$sha (source hardware address) - n bytes,
                                       where n is from the ar$hln field.
      ar$spa (source protocol address) - m bytes,
                                       where m is from the ar$pln field.
      ar$tha (target hardware address) - n bytes
      ar$tpa (target protocol address) - m bytes

   ar$hrd, ar$pro, ar$hln and ar$pln are the same as in regular ARP
   (see [1]).

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The differences are -&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The EtherType is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x8035&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x0806&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The Op codes are 3 &amp;amp; 4 (instead of the 1 &amp;amp; 2 ARP op codes)
    &lt;ul&gt;
      &lt;li&gt;3 - Reverse ARP request&lt;/li&gt;
      &lt;li&gt;4 - Reverse ARP reply&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was easily doable with Ostinato by using &lt;strong&gt;packet field overrides&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;We start with creating a normal ARP packet in Ostinato -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/arp-rarp/arp-packet.png&quot; alt=&quot;ARP packet&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To override the EtherType is straight-forward -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/arp-rarp/ethertype-override.png&quot; alt=&quot;Override Ethertype&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The ARP op code input box seems to offer only op code 1 and 2.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/arp-rarp/arp-config.png&quot; alt=&quot;ARP config&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But actually you can &lt;strong&gt;just type in any op code value&lt;/strong&gt; (&lt;em&gt;yeah, I realize this is not very obvious&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;In this case we need 3 (RARP Request) -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/arp-rarp/arp-opcode-override.png&quot; alt=&quot;Override ARP op code&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;💡 &lt;strong&gt;TIP&lt;/strong&gt;: You can &lt;a href=&quot;https://userguide.ostinato.org/stream-config/#overriding-fields&quot;&gt;override &lt;strong&gt;ANY&lt;/strong&gt; packet field&lt;/a&gt; even if the protocol doesn’t seem to offer a way to do that!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That’s it!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can now successfully send a valid RARP packet using Ostinato that can be verified using Wireshark -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/arp-rarp/rarp-wireshark.png&quot; alt=&quot;RARP in Wireshark&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ostinato-14-rarp-support&quot;&gt;Ostinato 1.4 RARP support&lt;/h2&gt;

&lt;p&gt;I have now added RARP support to Ostinato and this will be available starting v1.4.&lt;/p&gt;

&lt;p&gt;All you need is to select the appropriate RARP op code and the EtherType will be automatically updated to RARP -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/arp-rarp/rarp-config.png&quot; alt=&quot;RARP config&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Wed, 28 Feb 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-rarp/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-rarp/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato goes Turbo at 100Gbps</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;em&gt;Happy New Year!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In true geek fashion, I spent the Christmas holidays tinkering - this time on the Ostinato network traffic generator’s high-speed add-on - &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;Turbo Transmit&lt;/a&gt;. And I’m happy to share that &lt;strong&gt;Ostinato Turbo can now generate 100Gbps&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/turbo-100g-perf.png&quot; alt=&quot;Ostinato performance - Base and Turbo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was the trick you ask?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Horizontal scaling - using multiple cores.&lt;/p&gt;

&lt;h1 id=&quot;the-scaling-problem&quot;&gt;The scaling problem&lt;/h1&gt;
&lt;p&gt;The base Ostinato software can use a max of &lt;strong&gt;only one core per port&lt;/strong&gt; to transmit packets. Trying to transmit packets from multiple cores to a single ethernet port runs into &lt;strong&gt;contention&lt;/strong&gt; at the kernel, driver and NIC thus not delivering the desired performance increase. There wasn’t much Ostinato could do about this, but it’s something that needed to be solved at the lower layers.&lt;/p&gt;

&lt;p&gt;NIC manufacturers know about this problem and when designing high-speed NICs - 10Gbps or higher, they introduced &lt;strong&gt;multiple TX and RX queues&lt;/strong&gt; (&lt;em&gt;or sometimes combined TX+RX queues&lt;/em&gt;) for each port e.g. here’s Intel 40G XL710 -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ethtool -l enp4s0f0np0
Channel parameters for enp4s0f0np0:
Pre-set maximums:
RX:		0
TX:		0
Other:		0
Combined:	16
Current hardware settings:
RX:		0
TX:		0
Other:		0
Combined:	2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the hardware supported multiple queues, the Linux kernel and drivers were updated to utilize that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voila! No contention&lt;/strong&gt; - as long as we use one CPU core per NIC queue!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can now use multiple CPU cores independently.&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;ostinato-turbo&quot;&gt;Ostinato Turbo&lt;/h1&gt;
&lt;p&gt;The Ostinato Turbo add-on was architected to utilize multiple queues per port (and therefore multiple cores) from day one, but till now used only one core and one queue because as it turned out that was enough - thanks to the &lt;strong&gt;magic of AF_XDP fast path&lt;/strong&gt; which delivered a &lt;strong&gt;whopping 20 million packets/sec with just a single core&lt;/strong&gt; (for reference 10Gbps at 64 bytes is 14.8 Mpps). The catch - the NIC driver must support AF_XDP zero-copy which Intel and a few other NICs do.&lt;/p&gt;

&lt;p&gt;In November, a customer asked me if Ostinato could do 100Gbps and I told him Turbo could do 100Gbps at 600 byte packet sizes (or higher) on the Intel E810 NIC. How, you ask? &lt;em&gt;Because 100Gbps at 600 bytes comes to 20 Mpps - our single core/queue limit!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The customer was ok with this and went ahead. Here’s a screenshot from their test -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/100g-600bytes.png&quot; alt=&quot;100Gbps for 600 byte packets using 1 CPU core&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At around the same time, another customer reached out and asked about &lt;strong&gt;10Gbps on AWS/Azure&lt;/strong&gt;. My tests on those platforms revealed that although both supported AF_XDP, but not zero-copy - the max single core performance was only about 1 million packets/sec. For 10Gbps that works out to a minimum &lt;strong&gt;packet size of 1230 bytes to achieve 10Gbps&lt;/strong&gt; which was not acceptable to the customer.&lt;/p&gt;

&lt;h1 id=&quot;going-multi-core&quot;&gt;Going multi-core&lt;/h1&gt;
&lt;p&gt;It was time to extend Turbo to use multiple queues and cores - something I had designed-in but not implemented yet.&lt;/p&gt;

&lt;p&gt;The first challenge was how to modify the highly-optimized and somewhat complex packet Tx code to utilize multiple cores - this took only a few days of experimenting.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/tweets-packets-across-cores.png&quot; alt=&quot;Packets across cores&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The second challenge was to sync the Tx across cores after each stream - implementing a thread barrier solved that problem.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/tweets-sync-threads.png&quot; alt=&quot;Sync TX threads&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The last big challenge was to ensure per-stream stats and latency/jitter work across cores - a challenge again because of the highly-optimized and complex code for the same.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/tweets-stream-stats.png&quot; alt=&quot;Multi core stream stats&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Before Christmas eve I had all the core functionality working and the productivity continued post Christmas too – till unexpectedly, I had to take a few days off.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/tweets-productivity-injury.png&quot; alt=&quot;Productivity and injury&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Getting back into the groove proved to be a bit of a challenge - having to update the turbo-tune bash script didn’t help (&lt;em&gt;No, I don’t enjoy bash scripting - even with ChatGPT help!&lt;/em&gt;). I was distracted and procrastinating.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/resistance-at-end.png&quot; alt=&quot;War of Art&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once I got through that slump and I thought I’m almost done, I hit a pesky race condition for some cases. You know how much fun those are!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/tweets-race-condition.png&quot; alt=&quot;Multi core race condition&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All in all, &lt;strong&gt;I got everything done in about 4 weeks&lt;/strong&gt; - which I must admit I’m quite proud of and a testament to architecting Turbo right to make this multi-core work easy.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/100g-git-log.jpg&quot; alt=&quot;55 commits over 4 weeks to implement multi-core TX in Ostinato&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;With these changes you need just 8 CPU cores to generate 100Gbps traffic at 64 bytes on a AF_XDP zero-copy supported NIC such as the Intel E810!&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I posted almost-daily updates on a &lt;a href=&quot;https://twitter.com/pstavirs/status/1735621647464579430&quot;&gt;build-in-public Twitter thread&lt;/a&gt; all through those weeks - as you can see from the screenshots above. This is something I’ve been doing off late for all big and small Ostinato features and if you are interested in Ostinato development, you should &lt;a href=&quot;https://twitter.com/intent/follow?screen_name=pstavirs&quot;&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;10gbps-on-the-public-cloud&quot;&gt;10Gbps on the public cloud&lt;/h1&gt;
&lt;p&gt;The multi-core Turbo enables not just 100Gbps on physical NICs but also &lt;strong&gt;10Gbps on AWS and Azure cloud VMs&lt;/strong&gt; (&lt;em&gt;Google Cloud should also work, but I haven’t got around to test on it yet&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Here are my results on a Azure cloud VM -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-100gbps/10gbps-on-azure.png&quot; alt=&quot;10Gbps on Azure cloud VM&quot; /&gt;&lt;/p&gt;

&lt;p&gt;An important point to note about the cloud VMs is that all of them &lt;strong&gt;impose a aggregate bandwidth limit&lt;/strong&gt; across all interfaces of the VM instance which is typically &lt;strong&gt;lower than the interface link speed&lt;/strong&gt;. This bandwidth limit varies according to instance type, so you have to choose a VM/instance type accordingly!&lt;/p&gt;

&lt;h1 id=&quot;turbo-system-requirements&quot;&gt;Turbo System Requirements&lt;/h1&gt;
&lt;p&gt;With this newly acquired ability to scale TX across multiple CPU cores - the system requirements to run Turbo are much simpler, cheaper and accessible now - &lt;strong&gt;all you need is a NIC (or vNIC) that supports multiple queues and a multi-core CPU!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adopt &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;Ostinato Turbo&lt;/a&gt; today and &lt;strong&gt;save your team and your company a gazillion dollars&lt;/strong&gt; in test equipment!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Questions?&lt;/strong&gt; Let me know in the comments!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you found this interesting, help me spread the word by sharing this article using the share buttons below!&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 23 Jan 2024 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-100gbps/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-100gbps/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato show reserved ports broken in 1.3.0</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Close on the heels of releasing &lt;a href=&quot;https://userguide.ostinato.org/changelog/#2023-10-19-version-130&quot;&gt;Ostinato v1.3.0&lt;/a&gt;, I discovered a regression - the &lt;a href=&quot;https://userguide.ostinato.org/port-config/#reservation&quot;&gt;&lt;em&gt;Show Reserved Ports Only&lt;/em&gt;&lt;/a&gt; feature is broken. When this option is selected in v1.3.0 - the port list is shown correctly, but the port statistics is empty.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-rsvd-ports-broken/port-stats-broken.png&quot; alt=&quot;Show reserved ports broken&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On investigation, it looks like this regression was introduced as part of the &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-port-stats-ui/&quot;&gt;Port Stats UI improvement&lt;/a&gt; changes that went into 1.3.0. It was some minor code refactoring that I did, but apparently failed to verify. The fix has now been committed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-rsvd-ports-broken/port-stats-fixed.png&quot; alt=&quot;Show reserved ports fixed&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;workaround&quot;&gt;Workaround&lt;/h2&gt;
&lt;p&gt;Since the fix will only be available in 1.4.0 which is several months away, Ostinato 1.3.0 users can use the &lt;em&gt;Ports Stats Config View&lt;/em&gt; as a workaround till then.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-rsvd-ports-broken/port-stats-view-filter-icon.png&quot; alt=&quot;Port Stats View Filter Icon&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click on the icon shown above to open the below dialog -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-rsvd-ports-broken/port-stats-view-filter-dialog.png&quot; alt=&quot;Port Stats View Filter Dialog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The left-side list is the list of ports that is NOT shown and the right-side list is the list of ports that is shown in the port stats window. You can move the ports from one list to the other by selecting one or more ports in either list and using the left/right arrow icons.&lt;/p&gt;

&lt;p&gt;Yes, it’s a few more clicks unfortunately, so here are some tips -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You can shift-click and ctrl-click to select multiple ports in either list&lt;/li&gt;
  &lt;li&gt;You can double-click a port on either list to move it to the other list&lt;/li&gt;
  &lt;li&gt;You can drag the ports in the shown (i.e. right-side) list to change the order in which they appear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;e.g. using the above, if you have reserved only a few ports, shift-select all shown ports (right-side list) and press ← (left arrow icon) and then double-click on the reserved ports in the left-side list to add them to the shown (right-side) list.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-rsvd-ports-broken/port-stats-filter-dialog.gif&quot; alt=&quot;Port Stats View Filter Example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Found any other 1.3.0 regression? Let me know in the comments!&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 14 Nov 2023 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-show-reserved-ports-broken/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-show-reserved-ports-broken/</guid>
        </item>
    
  
    
	<item>
          <title>Latency and Jitter test</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Like most products, the number of new features requested for Ostinato is long. The effort to implement these also vary widely. Earlier this year I decided to implement one &lt;strong&gt;big&lt;/strong&gt; feature for the next Ostinato version and I zeroed in on &lt;em&gt;latency and jitter measurement&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The reason why this is big is because this requires timestamping packets which is an infra piece not yet supported in Ostinato. Also, doing this means changing the very core of the highly optimized packet transmission code in Ostinato which has not any major changes for almost 8-10 years (&lt;em&gt;for good reason!&lt;/em&gt;) and any changes here need to be done carefully.&lt;/p&gt;

&lt;p&gt;I estimated about a month to get this done for the base code after which I would have to do the same for &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;Turbo&lt;/a&gt; which is a different codebase.&lt;/p&gt;

&lt;p&gt;From a requirements point of view, I decided to implement this as a &lt;strong&gt;per-stream metric&lt;/strong&gt; similar to packet loss, traffic rates and other existing &lt;a href=&quot;&quot;&gt;per-stream stats&lt;/a&gt;. No additional configuration is needed beyond the basic stream stats configuration.&lt;/p&gt;

&lt;h1 id=&quot;basic-questions&quot;&gt;Basic questions&lt;/h1&gt;
&lt;p&gt;Before starting design and development, I needed to answer some fundamental questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. As a software traffic generator, would software (kernel/user level) timestamps be acceptable?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ping/iperf timing figures seem to be acceptable for a lot of users which are also based on software timestamps, so I made an assumption this will be the case for Ostinato too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. Should TX timestamps be stored within the packet or only locally at the TX agent?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I decided to go with the latter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. Should all TX packets be timestamped?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No, that’s likely to be too much additional cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. In that case, how often should TX packets be timestamped?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ietf.org/rfc/rfc2544.txt&quot;&gt;RFC 2544&lt;/a&gt; suggests 30 sec while the commercial hardware traffic generators likely do it for every packet. 30 seconds seemed too long for me and 1 second too short (&lt;em&gt;again from a cost point of view&lt;/em&gt;), so I arbitrarily chose a 5 seconds interval&lt;/p&gt;

&lt;h1 id=&quot;design-and-challenges&quot;&gt;Design and challenges&lt;/h1&gt;
&lt;p&gt;To identify timestamped packets, I added a TimingTag (TTAG) to the &lt;a href=&quot;https://userguide.ostinato.org/stream-stats/#how-does-it-work&quot;&gt;packet signature&lt;/a&gt; used for stream stats. The TTAG is essentially a &lt;strong&gt;unique identifier inserted on-the-fly in a packet every 5 seconds&lt;/strong&gt; to correlate TX and RX timestamps for the same packet.&lt;/p&gt;

&lt;p&gt;For these TTAG packets the TCP/UDP checksum would be incorrect unless we fix that as well on-the-fly. I spent a lot of time experimenting and finding ways to do this with least cost at Tx time only to discover much later that my approach would fail for interleaved streams. So, for the time being, I decided to live with incorrect checksums for TTAG packets - these are sent out only once every 5 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However I made sure sure that these packets do get counted at the RX end even if they have an incorrect TCP/UDP checksum.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The other challenge was that in case of multiple streams - in &lt;a href=&quot;https://userguide.ostinato.org/port-config/#transmit-mode&quot;&gt;sequential or interlaved&lt;/a&gt; transmit mode - the one TTAG packet every 5 seconds should be for &lt;em&gt;each TX stream&lt;/em&gt;. The stream rates, duration and packet counts are all user configured. After several misteps and a few different prototypes, I landed on something that worked for both modes and was performant.&lt;/p&gt;

&lt;p&gt;On the RX side, we have to capture and parse each packet to identify if it is a TTAG packet and if so, record the RX timestamps. Fortunately, we already capture all stream stats signature packets and parse them. I just extended the code to parse the TTAG also and record the Tx timestamp.&lt;/p&gt;

&lt;p&gt;Now that we have both TX and RX timestamps, we can diff the two and calculate the latency on a per-stream basis - we keeping doing this over the entire duration of the transmit to get the &lt;strong&gt;average latency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And finally the protobuf API additions and GUI code to include the latency value from agent to controller as part of stream stats and to display the same in the stream stats window.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-latency-jitter/stream-stats-latency.png&quot; alt=&quot;Stream Stats with Latency&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next I had to do everything for the &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;high-speed Turbo&lt;/a&gt; code base which is completely different than the base code. Moreover it has two completely different TX algos - one for low-speed high accuracy and another for high-speed high-throughput and changes were required for both.&lt;/p&gt;

&lt;p&gt;All the while making these changes, I had to stop and refactor existing code multiple times to fit in the new code. And at other times rewrite the new code to be more optimal so as not to affect performance of Tx rates. Of course, I introduced a few bugs while making these changes - &lt;em&gt;hopefully, I’ve caught and fixed ‘em all&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;With latency available, calculating jitter was straight-forward.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-latency-jitter/stream-stats-latency-jitter.png&quot; alt=&quot;Stream Stats with Latency and Jitter&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the end, it took more than 2.5K lines of code spread over 45 files and almost 4 months. &lt;em&gt;So much for my estimate of 1 month!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-latency-jitter/feature-dev-stats.png&quot; alt=&quot;Ostinato latency development stats&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you may have guessed from the above screenshot, this feature was &lt;a href=&quot;https://twitter.com/pstavirs/status/1646131839189532672&quot;&gt;built in public&lt;/a&gt; with regular updates over the 4 months posted to a twitter thread.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/intent/follow?screen_name=pstavirs&quot;&gt;Follow me on Twitter&lt;/a&gt; for more such build in public features in the future.&lt;/p&gt;

&lt;p&gt;Per-stream latency and jitter will be available in the next Ostinato version - v1.3.&lt;/p&gt;
</description>
          <pubDate>Wed, 19 Jul 2023 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-latency-jitter/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-latency-jitter/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato Port Stats UI improvements</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Recently Ostinato user &lt;em&gt;Ezra&lt;/em&gt; gave me some feedback about the Ostinato Port Stats UI -&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I find it very unreadable when for every parameter there is Rx and bellow Tx.&lt;/p&gt;

  &lt;p&gt;I think it would be much clearer if all TX (byte, packets, whatever) will be on top and then all Rx (packets, byte, etc..) will be below.&lt;/p&gt;

  &lt;p&gt;I would order it as bellow. With the current order it is very difficult to see in a glance the parameter I am interested. I have to read all and even worse, the lines don’t start with Receive or Transmit so I must read the entire text to find what I am looking for. Please start every line with Rx or Tx and if possible, please collect them as I recommended._&lt;/p&gt;

  &lt;p&gt;Tx rate Byte/Sec&lt;br /&gt;
Tx rate Bit/Sec&lt;br /&gt;
Tx rate Frames/Sec&lt;br /&gt;
Tx Frames (total)&lt;br /&gt;
—– please leave a space here ——&lt;br /&gt;
Rx rate Byte/Sec&lt;br /&gt;
Rx rate Bit/Sec&lt;br /&gt;
Rx rate Frames/Sec&lt;br /&gt;
Rx Frames (Total)&lt;br /&gt;
—- please leave a space here —–&lt;br /&gt;
Rx Errors&lt;br /&gt;
Rx Drop Errors&lt;br /&gt;
Rx Fifo Errors&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For reference, here’s the current Port Stats UI which Ezra is referring to.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-port-stats-ui/original.png&quot; alt=&quot;Original&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One of the main reasons users look at the port stats is to compare the Tx and Rx stats on 2 or more ports, so having them close to each other, I feel is better than grouping all Tx together followed by all Rx.&lt;/p&gt;

&lt;p&gt;So, I started by fixing the order, so that send and receive for all stats are always consistent (&lt;em&gt;it wasn’t already for some inexplicable reason&lt;/em&gt;) - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receive&lt;/code&gt;. I also removed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Byte Send/Receive Rate&lt;/code&gt; stats as networking users deal in bitrates rather than byte-rates.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-port-stats-ui/reorder-stats.png&quot; alt=&quot;Reorder stats&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, I extended the alternate background color of the stats to the row headers as well for more of a visual cue.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-port-stats-ui/alt-color-headers.png&quot; alt=&quot;Alt color headers&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A nice side-effect of doing the above that emerges is that the &lt;em&gt;send&lt;/em&gt; rows all have a gray background, while the &lt;em&gt;receive&lt;/em&gt; rows (except the drop/error ones) all have a white background.&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;This alternate header row colors doesn’t work on Windows because, by default, Qt will use a platform native look and Windows doesn’t allow alternate background color of header rows. A workaround is to &lt;em&gt;emulate&lt;/em&gt; the Windows style by starting Ostinato as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ostinato -style windows&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I then visually grouped the related stats using a horizontal line.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-port-stats-ui/visual-groups.png&quot; alt=&quot;Visual groups&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Extending the horizontal line to the row headers would be nice, but was too complicated to implement, so I let that slide.&lt;/p&gt;

&lt;p&gt;Finally, I moved on to Ezra’s suggestion to rename the stats starting withTx/Rx.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-port-stats-ui/txrx.png&quot; alt=&quot;Tx Rx&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Using the Tx/Rx terminology instead of Send/Receive made the pair names the same length and differ only in one character - in my opinion this makes it difficult to quickly distinguish between them.&lt;/p&gt;

&lt;p&gt;So I reverted the changes and retained the existing names, just rephrasing them so that they all start with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Send&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Receive&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-port-stats-ui/final.png&quot; alt=&quot;Final&quot; /&gt;&lt;/p&gt;

&lt;p&gt;What do you think of these changes? Let me know in the comments below!&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Mon, 13 Mar 2023 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-port-stats-ui/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-port-stats-ui/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato RX stream stats always zero</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Ostinato user &lt;em&gt;Leslie&lt;/em&gt; reached out to me to report that &lt;a href=&quot;https://userguide.ostinato.org/stream-stats/&quot;&gt;Ostinato per-stream stats&lt;/a&gt; are not working on the RX side (always 0) with VLAN-tagged packets on Ubuntu 22.04 LTS.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-no-rx-stream-stats.png&quot; alt=&quot;Ostinato 0 rx stream stats&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Upon investigation, it turns out to be a libpcap issue.&lt;/p&gt;

&lt;p&gt;But first, a bit of background.&lt;/p&gt;

&lt;p&gt;To implement per-stream statistics, Ostinato tags these packets with the magic value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x1d10c0da&lt;/code&gt; as the last 4 bytes of the packet, just before the Ethernet FCS. This serves as a signature to quickly check if it’s an Ostinato generated packet. We can then read the stream GUID which is encoded just before the signature.&lt;/p&gt;

&lt;p&gt;To increase performance, Ostinato uses a &lt;a href=&quot;https://www.tcpdump.org/manpages/pcap-filter.7.html&quot;&gt;pcap filter&lt;/a&gt; to read into user space only packets with the Ostinato signature. Simply put, we need to use the following capture filter -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ether[len - 4:4] == 0x1d10c0da
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, there’s a problem with this scheme. Say, we send out these packets with signature. Somewhere in transit or at the destination, the packet encounters an error and a ICMP error is sent back. The ICMP error packet contains the original packet as payload and so the ICMP error packet will also have the Ostinato signature at the end!&lt;/p&gt;

&lt;p&gt;To avoid matching these packets, we modify our capture filter as -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(ether[len - 4:4] == 0x1d10c0da) and not icmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But that matches only untagged packets, not vlan-tagged packets; so we modify the capture filter as -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(ether[len - 4:4] == 0x1d10c0da) and not (icmp or (vlan and icmp))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that’s where we run into the libpcap problem.&lt;/p&gt;

&lt;p&gt;The BPF instructions generated by libpcap for the above filter are incorrect!&lt;/p&gt;

&lt;p&gt;(&lt;em&gt;Note that the actual capture filter string used by Ostinato is a bit more complicated for reasons not relevant to this blog post&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;More details about the generated BPF for this problem and discussions with Wireshark and libpcap teams are available at the below links, if you’re curious -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://ask.wireshark.org/question/30724/capture-filter-not-working-due-to-incorrect-bpf/&quot;&gt;Wireshark Q&amp;amp;A&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/the-tcpdump-group/libpcap/issues/1162&quot;&gt;libpcap bug report&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem is Linux specific (likely due to BPF extensions) and is seen only for vlan tagged packets.&lt;/p&gt;

&lt;p&gt;Ubuntu 22.04 includes libpcap 1.10.1. The problem is present even with 1.9.x and 1.8.x. libpcap 1.7.x introduced the BPF vlan check (although the code looks very different).&lt;/p&gt;

&lt;p&gt;So what’s the solution?&lt;/p&gt;

&lt;p&gt;While I figure out how best to handle this in Ostinato, for now the solution is to use libpcap 1.6.2. Here are instructions to build that from source -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://www.tcpdump.org/release/libpcap-1.6.2.tar.gz
tar zxvf libpcap-1.6.2.tar.gz
cd libpcap-1.6.2
./configure
make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That will generate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libpcap.so.1.6.2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But Ostinato usually is linked against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libpcap.so.0.8&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ldd drone | grep libpcap&lt;/code&gt;). So we create a softlink by that name in the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libpcap-1.6.2&lt;/code&gt; folder.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ln -s libpcap.so.1.6.2 libpcap.so.0.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next step is to make Ostinato use this libpcap than the system one. For that we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LD_LIBRARY_PATH&lt;/code&gt; when starting Ostinato (or drone).&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;LD_LIBRARY_PATH=/path/to/libpcap-1.6.2/ ostinato
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Starting Ostinato like that should fix the rx stream stats problem.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Mon, 27 Feb 2023 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-rx-stream-stats-zero/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-rx-stream-stats-zero/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato in a container</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;For a long time now, I’ve been wanting to package Ostinato as a container. But never got around to it.&lt;/p&gt;

&lt;p&gt;Until now.&lt;/p&gt;

&lt;p&gt;As 2022 came to a close, I took the plunge, installed &lt;a href=&quot;https://docker.com&quot;&gt;Docker&lt;/a&gt;, read a few tutorials and created my first container, packaging the Ostinato agent &lt;em&gt;Drone&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Packaging the agent (&lt;em&gt;Drone&lt;/em&gt;) and the Ostinato GUI together took a few days as I needed to figure out how best to package a X windows GUI app inside a container — I decided to take the VNC route as it’s something Ostinato users (e.g. those using it with GNS3/EVE-NG/CML) are familar with.&lt;/p&gt;

&lt;p&gt;Ostinato being a Qt GUI app, my first attempt was to use the Qt built-in VNC server (just pass the arg &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-platform vnc&lt;/code&gt; to any Qt GUI app). While that worked, the dialogs and windows were not resizable — probably because I hadn’t included a Window Manager in the image. I would also need to package in &lt;a href=&quot;https://wireshark.org&quot;&gt;Wireshark&lt;/a&gt; to view capture files, so a minimal desktop or window manager was anyway called for. I went ahead with FLWM — the same one that the Ostinato VM uses. Now with multiple apps and a GUI desktop environment, I needed a full blown VNC server instead of the Qt built-in one. After a bit (read too much!) of research, I settled on x11vnc.&lt;/p&gt;

&lt;p&gt;After much reading of docs and some experimentation, I now had 2 Ostinato docker containers -&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker images
REPOSITORY                    TAG        IMAGE ID       CREATED       SIZE
ostinato/ostinato             v1.2.0-1   11ab5ac21cbe   3 days ago    751MB
ostinato/agent                v1.2.0-1   c1be11ba26fd   3 days ago    204MB
&amp;lt;none&amp;gt;                        &amp;lt;none&amp;gt;     c096bf3cb0da   3 days ago    206MB
&amp;lt;none&amp;gt;                        &amp;lt;none&amp;gt;     2a0175a90aba   5 days ago    754MB
&amp;lt;none&amp;gt;                        &amp;lt;none&amp;gt;     edc5c451a16f   6 days ago    751MB
&amp;lt;none&amp;gt;                        &amp;lt;none&amp;gt;     908a68fb0900   6 days ago    754MB
ubuntu                        22.04      6b7dfa7e8fdb   4 weeks ago   77.8MB
ghcr.io/nokia/srlinux         latest     db41df9a3ad0   5 weeks ago   2.8GB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next step was to try the Ostinato container with &lt;a href=&quot;https://containerlab.dev&quot;&gt;Containerlab&lt;/a&gt; which was fairly straightforward (except for some SR Linux config snippet that the containerlab discord members kindly helped me with) -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-clab.png&quot; alt=&quot;Ostinato in containerlab&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ostinato.org/pricing/clab&quot;&gt;Ostinato for Containerlab&lt;/a&gt; is now available on the Ostinato website.&lt;/p&gt;

&lt;p&gt;For more bespoke environments, the generic containers for the latest Ostinato release (v1.2.0) are now available to all &lt;a href=&quot;https://ostinato.org/pricing#superBundle&quot;&gt;Ostinato Super Bundle&lt;/a&gt; subscribers.&lt;/p&gt;

&lt;p&gt;How will &lt;strong&gt;you&lt;/strong&gt; use the Ostinato container? Let me know in the comments below.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 10 Jan 2023 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-containers/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-containers/</guid>
        </item>
    
  
    
	<item>
          <title>Distinguish field names for combo protocols</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A few days ago, Ostinato user &lt;a href=&quot;https://twitter.com/emperphis&quot;&gt;João&lt;/a&gt; reached out on Twitter and requested for a way to distinguish between SVLAN and CVLAN fields for a VlanStack protocol when trying to configure the vlan field to &lt;a href=&quot;https://userguide.ostinato.org/stream-config/#variable-fields&quot;&gt;vary across packets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/emperphis/status/1600943800670560256&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-prefix-combo-fields/field-diff-req-tweet.png&quot; alt=&quot;Feature Request&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I made some code changes for “combo” protocols such as VlanStack (SVLAN+VLAN), IPv4over4, IPv4over6 etc.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-prefix-combo-fields/prefix-vlanstack-fields.png&quot; alt=&quot;VlanStack fields&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For cases such as IP 4over4, the prefix &lt;em&gt;Outer&lt;/em&gt;/&lt;em&gt;Inner&lt;/em&gt; is used instead.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-prefix-combo-fields/prefix-ip4o4-fields.png&quot; alt=&quot;IP4over4 fields&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These changes will also reflect in the &lt;em&gt;Packet View&lt;/em&gt; and &lt;em&gt;Find &amp;amp; Replace&lt;/em&gt; as well.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-prefix-combo-fields/packet-view-combo-fields.png&quot; alt=&quot;Combo fields in packet view&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-prefix-combo-fields/find-replace-combo-fields.png&quot; alt=&quot;Combo fields in Find &amp;amp; REplace&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These changes will be part of the next Ostinato v1.3.&lt;/p&gt;

&lt;p&gt;If something in Ostinato is troubling you or if you are missing a feature in Ostinato that would make you more effective, reach out via &lt;a href=&quot;https://twitter.com/ostinato&quot;&gt;Twitter&lt;/a&gt; or the &lt;a href=&quot;https://groups.google.com/forum/#!forum/ostinato&quot;&gt;Forum&lt;/a&gt; and I’ll try to implement it.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 20 Dec 2022 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-combo-field-names/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-combo-field-names/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato 1.2.0 released</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;It’s been more than 2 years since the last Ostinato release - v1.1.&lt;/p&gt;

&lt;p&gt;I’m happy to announce the release of Ostinato 1.2.0 with many new features.&lt;/p&gt;

&lt;p&gt;Here are some of them -&lt;/p&gt;

&lt;h2 id=&quot;-send-ipgre-packets&quot;&gt;🧱 Send IP/GRE packets&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/v1.2.0/gre.png&quot; alt=&quot;New GRE protocol&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;-new-per-stream-stats---duration-avg-txrx-pktbit-rates&quot;&gt;📈 New per-stream stats - duration, avg tx/rx pkt/bit rates&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/v1.2.0/per-stream-rates.png&quot; alt=&quot;per-stream duration and rates&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;-imix-traffic-made-easy&quot;&gt;🔀 IMIX traffic made easy&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-made-easy.png&quot; alt=&quot;Easy IMIX traffic&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-imix-traffic/&quot;&gt;Details&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;-bulk-edit-packets-using-find--replace&quot;&gt;🔎 Bulk edit packets using Find &amp;amp; Replace&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-find-replace.png&quot; alt=&quot;Packet Field find &amp;amp; replace&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-find-replace/&quot;&gt;Details&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;-port-traffic-rate-configuration-as-load-&quot;&gt;💯 Port traffic rate configuration as load %&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/v1.2.0/port-load.png&quot; alt=&quot;Port Load %&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/pstavirs/status/1456654864411357185&quot;&gt;Details&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;-export-pcap-with-nanosec-resolution&quot;&gt;🕑 Export PCAP with nanosec resolution&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/v1.2.0/pcap-nanosec.png&quot; alt=&quot;Save as pcap with nanosecond resolution&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-nsec-pcap/&quot;&gt;Details&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;-gui-themes---both-light-and-dark&quot;&gt;🎨 GUI Themes - both light and dark!&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-material-dark.png&quot; alt=&quot;Ostinato GUI Themes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-themes/&quot;&gt;Details&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;-a-bunch-of-bugfixes&quot;&gt;➕ a bunch of bugfixes!&lt;/h2&gt;

&lt;p&gt;You can check out the &lt;a href=&quot;https://userguide.ostinato.org/changelog/#2022-08-15-version-120&quot;&gt;full changelog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Questions? Suggestions? Feedback?&lt;/p&gt;

&lt;p&gt;Let me know in the comments below!&lt;/p&gt;

&lt;p&gt;Want to be notified about new Ostinato features as they are developed? &lt;a href=&quot;https://twitter.com/intent/user?screen_name=pstavirs&quot;&gt;Follow me&lt;/a&gt; on twitter where I tweet about Ostinato developement.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Mon, 29 Aug 2022 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-1.2.0/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-1.2.0/</guid>
        </item>
    
  
    
	<item>
          <title>Using Ostinato with mininet</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;I’d heard about &lt;a href=&quot;http://mininet.org/&quot;&gt;mininet&lt;/a&gt; off and on, but had never really tried it myself.&lt;/p&gt;

&lt;p&gt;Mininet is a Linux based network emulator which creates a network of virtual hosts, switches, controllers, and links all on a single Linux host. It does this using containers (namespaces essentially) and connects them using veth pairs.&lt;/p&gt;

&lt;p&gt;An &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; user was trying to use Ostinato with mininet and was not seeing flows on the OVS switch and so reached out to me. I used this opportunity to try mininet out.&lt;/p&gt;

&lt;p&gt;I installed the &lt;a href=&quot;http://mininet.org/download/&quot;&gt;mininet VM&lt;/a&gt; as recommended. The VM doesn’t have a desktop environment installed and the recommendation is to use X11 forwarding to run GUI apps like Wireshark. Ostinato is also a GUI app, so will also require X11 forwarding.&lt;/p&gt;

&lt;p&gt;But, first a couple of notes about X11 forwarding for those using mininet VM on a Windows host.&lt;/p&gt;

&lt;p&gt;Both the native Windows ssh client and &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/wsl/about&quot;&gt;WSL&lt;/a&gt; ssh client were not able to do X11 forwarding - not sure if they don’t support it or I was doing something incorrect. I was able to use good ol’ &lt;a href=&quot;https://www.putty.org/&quot;&gt;Putty&lt;/a&gt; with X11 forwarding successfully.&lt;/p&gt;

&lt;p&gt;Both Wireshark and Ostinato are Qt based GUI apps. However Qt5 and Xming don’t play nicely (some error about XInput not being supported). I replaced Xming with &lt;a href=&quot;https://sourceforge.net/projects/vcxsrv/&quot;&gt;VcXsrv&lt;/a&gt; and was able to use that successfully.&lt;/p&gt;

&lt;p&gt;Back to mininet.&lt;/p&gt;

&lt;p&gt;Mininet has several predefined topologies with option to create custom ones. I went with the default minimal topo -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mininet@mininet-vm:~$ sudo -E mn
*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(h1, s1) (h2, s1)
*** Configuring hosts
h1 h2
*** Starting controller
c0
*** Starting 1 switches
s1 ...
*** Starting CLI:

mininet&amp;gt; net
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2
s1 lo:  s1-eth1:h1-eth0 s1-eth2:h2-eth0
c0

mininet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Visually, this is how the above topo looks -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;     c0
      | 
     s1
    /  \
   /    \
  h1    h2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h1-eth0&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h2-eth0&lt;/code&gt; interfaces are inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h2&lt;/code&gt; containers respectively while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s1-eth1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s2-eth2&lt;/code&gt; are in the default/root namespace and ifconfig on the mininet prompt and the VM prompt confirm the same.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mininet&amp;gt; h1 ifconfig
h1-eth0: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
        inet 10.0.0.1  netmask 255.0.0.0  broadcast 10.255.255.255
        ether 92:69:d5:2c:1b:d0  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

mininet&amp;gt; h2 ifconfig
h2-eth0: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
        inet 10.0.0.2  netmask 255.0.0.0  broadcast 10.255.255.255
        ether 16:0d:d7:bc:be:a8  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

mininet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mininet@mininet-vm:~$ ifconfig
eth0: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
        inet 192.168.1.35  netmask 255.255.255.0  broadcast 192.168.1.255
        ether 08:00:27:3c:f7:01  txqueuelen 1000  (Ethernet)
        RX packets 1573653  bytes 99181783 (99.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2759420  bytes 3961121460 (3.9 GB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 413122  bytes 3819486904 (3.8 GB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 413122  bytes 3819486904 (3.8 GB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

s1-eth1: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
        ether da:2b:00:64:0e:fa  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

s1-eth2: flags=4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
        ether 1e:4d:c5:e6:4d:ae  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To generate traffic into the switch, we need to run Ostinato inside the host containers, NOT in the root namespace. To do that, start an xterm on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h1&lt;/code&gt; and then start Ostinato from inside that xterm&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mininet&amp;gt; xterm h1
mininet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;notice--info&quot;&gt;If you don’t see the xterm window, make sure you started mininet by passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-E&lt;/code&gt; to sudo like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo -E mn&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;From the &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h1&lt;/code&gt; xterm window&lt;/strong&gt; (not the main mininet VM), start Ostinato -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@mininet-vm:~# ostinato &amp;amp;
root@mininet-vm:~#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;notice--info&quot;&gt;If you don’t see any ports in Ostinato, &lt;a href=&quot;https://ostinato.org/docs/faq#q-port-group-has-no-interfaces&quot;&gt;see this Ostinato FAQ&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure that Ostinato lists the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h1-eth0&lt;/code&gt; port.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-mininet.png&quot; alt=&quot;Ostinato with Mininet&quot; /&gt;&lt;/p&gt;

&lt;p&gt;From here on, create Ostinato &lt;a href=&quot;https://userguide.ostinato.org/streams/&quot;&gt;streams&lt;/a&gt;, &lt;a href=&quot;https://userguide.ostinato.org/device-emulation/&quot;&gt;devices&lt;/a&gt; and the usual Ostinato stuff.&lt;/p&gt;

&lt;p&gt;To examine flows on the openflow switch, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dpctl dump-flows&lt;/code&gt; on the mininet prompt -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mininet&amp;gt; dpctl dump-flows
*** s1 ------------------------------------------------------------------------
 cookie=0x0, duration=202.156s, table=0, n_packets=9929, n_bytes=595740, idle_timeout=60, priority=65535,udp,in_port=&quot;s1-eth1&quot;,vlan_tci=0x0000,dl_src=26:e5:29:01:96:5b,dl_dst=d6:41:7c:21:0b:02,nw_src=10.0.0.1,nw_dst=10.0.0.2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:&quot;s1-eth2&quot;
 cookie=0x0, duration=49.649s, table=0, n_packets=2434, n_bytes=146040, idle_timeout=60, priority=65535,tcp,in_port=&quot;s1-eth1&quot;,vlan_tci=0x0000,dl_src=26:e5:29:01:96:5b,dl_dst=d6:41:7c:21:0b:02,nw_src=10.0.0.1,nw_dst=10.0.0.2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:&quot;s1-eth2&quot;
 cookie=0x0, duration=49.636s, table=0, n_packets=2434, n_bytes=131436, idle_timeout=60, priority=65535,tcp,in_port=&quot;s1-eth2&quot;,vlan_tci=0x0000,dl_src=d6:41:7c:21:0b:02,dl_dst=26:e5:29:01:96:5b,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,tp_src=0,tp_dst=0 actions=output:&quot;s1-eth1&quot;
 cookie=0x0, duration=0.652s, table=0, n_packets=0, n_bytes=0, idle_timeout=60, priority=65535,icmp,in_port=&quot;s1-eth2&quot;,vlan_tci=0x0000,dl_src=d6:41:7c:21:0b:02,dl_dst=26:e5:29:01:96:5b,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,icmp_type=3,icmp_code=3 actions=output:&quot;s1-eth1&quot;
 cookie=0x0, duration=90.647s, table=0, n_packets=2, n_bytes=84, idle_timeout=60, priority=65535,arp,in_port=&quot;s1-eth1&quot;,vlan_tci=0x0000,dl_src=26:e5:29:01:96:5b,dl_dst=d6:41:7c:21:0b:02,arp_spa=10.0.0.1,arp_tpa=10.0.0.2,arp_op=2 actions=output:&quot;s1-eth2&quot;
 cookie=0x0, duration=44.568s, table=0, n_packets=1, n_bytes=42, idle_timeout=60, priority=65535,arp,in_port=&quot;s1-eth2&quot;,vlan_tci=0x0000,dl_src=d6:41:7c:21:0b:02,dl_dst=26:e5:29:01:96:5b,arp_spa=10.0.0.2,arp_tpa=10.0.0.1,arp_op=1 actions=output:&quot;s1-eth1&quot;

mininet&amp;gt; dpctl dump-flows tcp
*** s1 ------------------------------------------------------------------------
 cookie=0x0, duration=55.213s, table=0, n_packets=2706, n_bytes=162360, idle_timeout=60, priority=65535,tcp,in_port=&quot;s1-eth1&quot;,vlan_tci=0x0000,dl_src=26:e5:29:01:96:5b,dl_dst=d6:41:7c:21:0b:02,nw_src=10.0.0.1,nw_dst=10.0.0.2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:&quot;s1-eth2&quot;
 cookie=0x0, duration=55.200s, table=0, n_packets=2706, n_bytes=146124, idle_timeout=60, priority=65535,tcp,in_port=&quot;s1-eth2&quot;,vlan_tci=0x0000,dl_src=d6:41:7c:21:0b:02,dl_dst=26:e5:29:01:96:5b,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,tp_src=0,tp_dst=0 actions=output:&quot;s1-eth1&quot;

mininet&amp;gt; dpctl dump-flows udp
*** s1 ------------------------------------------------------------------------
 cookie=0x0, duration=210.107s, table=0, n_packets=10323, n_bytes=619380, idle_timeout=60, priority=65535,udp,in_port=&quot;s1-eth1&quot;,vlan_tci=0x0000,dl_src=26:e5:29:01:96:5b,dl_dst=d6:41:7c:21:0b:02,nw_src=10.0.0.1,nw_dst=10.0.0.2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:&quot;s1-eth2&quot;

mininet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://ostinato.org/features/&quot;&gt;full Ostinato arsenal of features&lt;/a&gt; is now available at your disposal to generate the traffic flows that you need to verify your SDN controller and openflow switch.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 22 Feb 2022 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-mininet/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-mininet/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato Themes</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Amongst mostly &lt;a href=&quot;https://www.capterra.com/p/231149/Ostinato/#reviews&quot;&gt;positive Ostinato reviews on Capterra&lt;/a&gt;, Ostinato user &lt;em&gt;Tamás&lt;/em&gt; mentions that the Ostinato UI looks “old school”. And I agree.&lt;/p&gt;

&lt;p&gt;Ostinato uses &lt;a href=&quot;https://www.qt.io&quot;&gt;Qt&lt;/a&gt; as the GUI toolkit. It uses Qt widgets and was built for Desktops before QtQuick for mobile first GUIs became available. Being the &lt;a href=&quot;https://ostinato.org/about/&quot;&gt;one-man-band&lt;/a&gt; of Ostinato and having the wisdom gleaned from being a programmer for 30+ years, I know that redoing the UI is not prudent (&lt;em&gt;much as I might want to start from a clean slate!&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;However, I knew that Qt offered ways to change the look and feel of the UI widgets using &lt;a href=&quot;https://doc.qt.io/qt-5/stylesheet-customizing.html&quot;&gt;Qt Style Sheets&lt;/a&gt; – CSS brought to desktop widgets. Now, while I know CSS concepts and can tweak a few things when pushed, I’m not really a front-end guy. Moreover, from experience I know that if I start on that road, I will end up spending days and weeks pixel tweaking!&lt;/p&gt;

&lt;p&gt;So, I looked for ready made QSS themes. What comes up as the first result is &lt;a href=&quot;https://qss-stock.devsecstudio.com&quot;&gt;QSS Stock&lt;/a&gt;. While it has several themes, they didn’t feel very professional to my eyes. Next, I looked for open source projects and found two that I liked.&lt;/p&gt;

&lt;p&gt;The first - &lt;a href=&quot;https://github.com/UN-GCPDS/qt-material&quot;&gt;qt-material&lt;/a&gt; aims to style Qt widgets as per the &lt;a href=&quot;https://material.io&quot;&gt;Material Design&lt;/a&gt; guidelines. The second was &lt;a href=&quot;https://github.com/ColinDuquesnoy/QDarkStyleSheet&quot;&gt;QDarkStyle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I made the necessary tweaks required to integrate their respective QSS files along with the icons with Ostinato. Here’s a video with the end results -&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/IQc6xa2Rc9Q&quot; frameborder=&quot;0&quot; allow=&quot;picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt; &lt;/iframe&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;There are some things that I’m not happy with and would like to change, but for the next version Ostinato 1.2, I plan to release it as it is and tag it as EXPERIMENTAL.&lt;/p&gt;

&lt;p&gt;What do you think? Share your thoughts and feedback in the comments below.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 08 Feb 2022 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-themes/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-themes/</guid>
        </item>
    
  
    
	<item>
          <title>Import and export PCAP files with nanosecond timestamps</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Ostinato has supported importing and exporting PCAP files since 2011. In 2020, I added support to import non-PCAP format (e.g. pcapng) capture files by converting them to PCAP files internally.&lt;/p&gt;

&lt;p&gt;Ostinato customer &lt;em&gt;Orhan&lt;/em&gt; recently reached out to me to investigate why an imported PCAP file when exported back to PCAP after editing the packets in Ostinato had incorrect timestamps.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/nsec-pcap-prob.png&quot; alt=&quot;Ostinato nsec pcap problem&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After debugging, I found that this was due to the input pcap file having &lt;strong&gt;nanosecond precision&lt;/strong&gt; timestamps while Ostinato only supports &lt;strong&gt;microsecond precision&lt;/strong&gt; timestamps (the original PCAP format) — in some cases.&lt;/p&gt;

&lt;p&gt;While a nanosecond PCAP was imported correctly in &lt;em&gt;intelligent mode&lt;/em&gt; and would replay with the same nanosecond accuracy to the extent possible, saving a PCAP always used microsecond timestamps.&lt;/p&gt;

&lt;p&gt;A nanosecond PCAP file imported in &lt;em&gt;raw&lt;/em&gt; (aka non-intelligent) mode lost its nanosecond precision and fell back to using microsecond timestamps.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/nsec-pcap.png&quot; alt=&quot;Ostinato nsec pcap&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Starting v1.2, Ostinato saves PCAP files with nanosecond precision timestamps. Also, importing a nanosecond pcap in raw mode is now natively supported without losing the timestamp precision.&lt;/p&gt;

&lt;p class=&quot;notice--warning&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Packet captures triggered from Ostinato, however, still use microsecond precision - this will be changed in a future version. A workaround, for now, is to directly use tcpdump (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--time-stamp-precision=nano&lt;/code&gt;) or tshark (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--time-stamp-type &amp;lt;type&amp;gt;&lt;/code&gt;) for capturing packets. Note that the NIC, OS and libpcap version (&amp;gt;= 1.0) should support nanosecond timestamps - many of them don’t.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 25 Jan 2022 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-nsec-pcap/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-nsec-pcap/</guid>
        </item>
    
  
    
	<item>
          <title>Edit PCAP using Ostinato</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A popular use case for Ostinato is to import and replay PCAP (packet capture) files - and that’s one reason why Ostinato is &lt;em&gt;Wireshark in Reverse&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Sometimes you need to edit the packet contents before you replay e.g. changing IP or MAC addresses, TCP/UDP port numbers etc. You can do that with Ostinato after you import the PCAP file.&lt;/p&gt;

&lt;p&gt;However, you need to edit each imported packet individually (each PCAP packet is imported as a Ostinato stream). This is doable if you have only a few packets to edit, but quickly gets painful and downright impossible if you have hundreds or thousands of packets that you need to rewrite.&lt;/p&gt;

&lt;p&gt;Here’s where tools like &lt;a href=&quot;http://bittwist.sourceforge.net/doc/bittwiste.1.html&quot;&gt;bittwiste&lt;/a&gt; and &lt;a href=&quot;https://tcpreplay.appneta.com/wiki/tcprewrite-man.html&quot;&gt;tcprewrite&lt;/a&gt; can help rewrite all packets as per your requirement. But they have their own limitations.&lt;/p&gt;

&lt;p&gt;Starting Ostinato v1.2, you can do this bulk rewrite of protocol fields directly within Ostinato using the new Find &amp;amp; Replace Feature. And you can &lt;strong&gt;rewrite ALL fields of ALL protocols&lt;/strong&gt; natively supported by Ostinato.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-protocols.png&quot; alt=&quot;Ostinato supported protocols&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The first thing to ensure though is that the &lt;em&gt;Recalculate Checksums&lt;/em&gt; option is selected when importing the PCAP file. Without this, when you replay edited packets, you will end up with packets being dropped due to incorrect checksums.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/pcap-import-options.png&quot; alt=&quot;Pcap import recalculate checksums&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once your packets are imported as Ostinato streams, select &lt;em&gt;Find &amp;amp; Replace&lt;/em&gt; from the streams context-menu to open the Find &amp;amp; Replace Dialog -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ostinato-find-replace.png&quot; alt=&quot;Find &amp;amp; Replace protocol fields&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The above image should be pretty much self-evident. You select the protocol and field that you want to change, enter the find and replace values and you are good to go. You can use match/replace masks for partial rewrites. See the &lt;a href=&quot;https://userguide.ostinato.org/find-replace/&quot;&gt;Ostinato find &amp;amp; replace documentation&lt;/a&gt; for further details.&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;💎 Find &amp;amp; Replace works not only for PCAP editing but also for Ostinato streams created from scratch&lt;/p&gt;

&lt;p&gt;Once you’ve rewritten the protocol fields, you can &lt;a href=&quot;https://userguide.ostinato.org/pcap-replay/#replay-timing&quot;&gt;replay and transmit&lt;/a&gt; the edited packets or &lt;a href=&quot;https://userguide.ostinato.org/stream-save-open/&quot;&gt;save them as a PCAP file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a video showing how Find &amp;amp; Replace works -&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/FWqRVAhEYqc&quot; frameborder=&quot;0&quot; allow=&quot;picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt; &lt;/iframe&gt;
&lt;p&gt;&lt;br /&gt;
For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Mon, 20 Dec 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-find-replace/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-find-replace/</guid>
        </item>
    
  
    
	<item>
          <title>Send IGMP packets</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;I have always written quick ‘n dirty scripts and small utilities for myself, all through my career and even earlier in college. As a matter of fact, &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; is one such tool that just kept growing bigger and bigger 😺!&lt;/p&gt;

&lt;p&gt;But before there was Ostinato, there was a IGMP packet generator that I wrote to verify IGMP Snooping on networking devices. I recently discovered the source code for this tool in my backups and am now releasing it as a free tool.&lt;/p&gt;

&lt;p&gt;Presenting &lt;strong&gt;sendigmp&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://sendigmp.com&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/sendigmp.png&quot; alt=&quot;Send Igmp&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 05 Oct 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/send-igmp/</link>
          <guid isPermaLink="true">https://srivatsp.com/send-igmp/</guid>
        </item>
    
  
    
	<item>
          <title>Generate IMIX traffic with Ostinato</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;a href=&quot;https://ostinato.org/&quot;&gt;Ostinato&lt;/a&gt; can craft packets and generate network traffic for various protocols at various packet sizes.&lt;/p&gt;

&lt;p&gt;But first things first.&lt;/p&gt;

&lt;h2 id=&quot;what-is-imix-traffic&quot;&gt;What is IMIX traffic?&lt;/h2&gt;
&lt;p&gt;IMIX or Internet Mix is a traffic profile representing &lt;em&gt;typical&lt;/em&gt; Internet traffic. It’s a mix of specific packet sizes and  number of packets of that size.&lt;/p&gt;

&lt;p&gt;Whether the traffic profile truly mirrors the Internet traffic is debatable, still having a standard IMIX profile is a useful benchmark. There’s no &lt;strong&gt;official standard&lt;/strong&gt; for IMIX - each vendor seems to have their own variant. For this post, we will go with &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Mix&quot;&gt;Simple IMIX&lt;/a&gt; as described in Wikipedia.&lt;/p&gt;

&lt;h2 id=&quot;simple-imix&quot;&gt;Simple IMIX&lt;/h2&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Eth Packet Size&lt;/td&gt;
      &lt;td&gt;No. of Packets&lt;/td&gt;
      &lt;td&gt;% Packet Distribution&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
  &lt;tfoot&gt;
    &lt;tr&gt;
      &lt;td&gt;64&lt;/td&gt;
      &lt;td&gt;7&lt;/td&gt;
      &lt;td&gt;58.33%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;594&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;33.33%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1518&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;8.33%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tfoot&gt;
&lt;/table&gt;

&lt;p&gt;The Wikipedia page specifies IP packet size. We derive the Ethernet packet size as follows&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ethernetPacketSize = max(ipPacketSize+18, 64)
  18 = size of (dmac + smac + ethtype + fcs)
  64 = minimum ethernet packet size
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s an average packet size of ~362 bytes.&lt;/p&gt;

&lt;h2 id=&quot;create-imix-streams-in-ostinato&quot;&gt;Create IMIX streams in Ostinato&lt;/h2&gt;

&lt;h3 id=&quot;with-ostinato-120-or-later&quot;&gt;With Ostinato 1.2.0 or later&lt;/h3&gt;

&lt;p&gt;In Ostinato 1.2.0 or later, simply select IMIX as the frame length mode,&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-made-easy.png&quot; alt=&quot;IMIX frame length mode &quot; /&gt;&lt;/p&gt;

&lt;p&gt;and change the number of packets to at least 12 (7+4+1).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-made-easy-count.png&quot; alt=&quot;IMIX mode frame count&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s it. Seriously!&lt;/p&gt;

&lt;p&gt;This method uses the &lt;em&gt;Simple IMIX&lt;/em&gt; distribution as specified above. If you want to use a different distribution, use the below method.&lt;/p&gt;

&lt;h3 id=&quot;with-ostinato-11-or-earlier&quot;&gt;With Ostinato 1.1 or earlier&lt;/h3&gt;
&lt;p&gt;We need to create 3 streams in Ostinato, one for each row in the Simple IMIX table above and send packets in a loop.&lt;/p&gt;

&lt;p&gt;If you prefer video, watch the IMIX demo from my talk at Sharkfest 2020.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/1DIs2VIT3bI?rel=0&amp;amp;start=2735&amp;amp;end=2947&amp;amp;autoplay=0&quot; frameborder=&quot;0&quot; allow=&quot;picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt; &lt;/iframe&gt;
&lt;p&gt;&lt;br /&gt;
Here are the detailed steps -&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create the first stream of frame length 64 and number of packets as 7 &lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-stream1-size-64B.png&quot; alt=&quot;IMIX stream #1 - 64 bytes&quot; /&gt;
    &lt;p&gt;&lt;/p&gt;
    &lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-stream1-count-7.png&quot; alt=&quot;IMIX stream #1 - 7 packets&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Configure the protocols and protocol fields for the stream as required&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Duplicate the above stream for a count of 2 &lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-duplicate-streams.gif&quot; alt=&quot;IMIX duplicate stream&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Rename the new streams as &lt;em&gt;IMIX stream 2&lt;/em&gt; and &lt;em&gt;3&lt;/em&gt;&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-renamed-streams.png&quot; alt=&quot;IMIX renamed streams&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Edit the second stream to set frame length 594 and number of packets as 4 &lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-stream2-size-594B.png&quot; alt=&quot;IMIX stream2 - 594 bytes&quot; /&gt;
    &lt;p&gt;&lt;/p&gt;
    &lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-stream2-count-4.png&quot; alt=&quot;IMIX stream2 - 4 packets&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Edit the third stream to set frame length 1518 and number of packets as 1 &lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-stream3-size-1518B.png&quot; alt=&quot;IMIX stream #3 - 1518 bytes&quot; /&gt;
    &lt;p&gt;&lt;/p&gt;
    &lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-stream3-count-1.png&quot; alt=&quot;IMIX stream #3 - 1 packet&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Set the third stream to loop back to first stream &lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-streams-loop.png&quot; alt=&quot;IMIX stream3 - goto first&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Click on &lt;em&gt;Apply&lt;/em&gt; followed by &lt;em&gt;Start Transmit&lt;/em&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-apply-start-tx.png&quot; alt=&quot;IMIX transmit&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h5 id=&quot;use-interleaved-streams-to-mix-up-the-imix-packets&quot;&gt;Use interleaved streams to mix up the IMIX packets&lt;/h5&gt;
&lt;p&gt;If you look at the Wireshark capture, you’ll notice that streams are sent in sequence, i.e. 7 packets of size 64, followed by 4 packets of size 594 and finally 1 packet of size 1518, before it repeats that same pattern&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-sequential-packets.png&quot; alt=&quot;IMIX sequential streams&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you are wondering why Wireshark shows 4 bytes less for all packet sizes, that’s because Ostinato configuration of packet size is including FCS, while Wireshark shows packet sizes without FCS (the NIC strips it off).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Although this sequence should not cause a problem, if you want to mix up the sizes, you can &lt;a href=&quot;https://userguide.ostinato.org/port-config/&quot;&gt;configure the port&lt;/a&gt; to use &lt;em&gt;Interleaved streams&lt;/em&gt; instead of &lt;em&gt;Sequential streams&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/common/ost-port-cfg.png&quot; alt=&quot;Port transmit mode&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In interleaved mode, all streams are transmitted simultaneously, so you will get a mix of the configured packet sizes, but sent in the same 7:4:1 ratio.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-interleaved-packets.png&quot; alt=&quot;IMIX interleaved streams&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that, after changing the port mode to interleaved, you have to edit the streams so that the &lt;em&gt;Packets/sec&lt;/em&gt; rate is in the 7:4:1 ratio. A quick way to do this is to set each stream pps rate to 7, 4 and 1 respectively and then change the &lt;em&gt;Avg pps&lt;/em&gt; rate from 12 to whatever value you want - any subsequent change to the &lt;em&gt;Avg pps&lt;/em&gt; rate will retain the 7:4:1 rate ratio across the streams.&lt;/p&gt;

&lt;h2 id=&quot;verify-imix-using-wireshark&quot;&gt;Verify IMIX using Wireshark&lt;/h2&gt;
&lt;p&gt;To verify, capture the packets in &lt;em&gt;Wireshark&lt;/em&gt;, go to Statistics | Packet Lengths and look at the &lt;em&gt;Percent&lt;/em&gt; column -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-imix/imix-wireshark-verify.png&quot; alt=&quot;IMIX wireshark verify&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Verify that the &lt;em&gt;Percent&lt;/em&gt; values match the &lt;em&gt;% Packet distribution&lt;/em&gt; in the Simple IMIX table above.&lt;/p&gt;

&lt;h2 id=&quot;gotchas&quot;&gt;Gotchas&lt;/h2&gt;
&lt;p&gt;Make sure your DUT/SUT supports packet size of 1518 - in my experiments with virtual topologies, VirtualBox would drop packets &amp;gt; 1504 bytes.&lt;/p&gt;

&lt;h2 id=&quot;other-imix-profiles&quot;&gt;Other IMIX profiles&lt;/h2&gt;
&lt;p&gt;If you want to try other IMIX profiles, see the &lt;a href=&quot;https://ostinato.org/guides/imix&quot;&gt;Ostinato IMIX Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 14 Sep 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-imix-traffic/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-imix-traffic/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato at FOSDEM 2021</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Earlier this year I gave a talk at FOSDEM 2021 on how to add more protocols to the &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato traffic generator&lt;/a&gt;. FOSDEM is one of Europe’s largest volunteer-driven open source and free software conferences.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ostinato.org/docs/fosdem2021&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-fosdem2021.png&quot; alt=&quot;Ostinato at FOSDEM 2021&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video and slides are posted on the &lt;a href=&quot;https://ostinato.org/docs/fosdem2021&quot;&gt;Ostinato website&lt;/a&gt;.&lt;/p&gt;
</description>
          <pubDate>Thu, 09 Sep 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/fosdem-2021/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/fosdem-2021/</guid>
        </item>
    
  
    
	<item>
          <title>How to generate Geneve encapsulated packets</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A decade or so ago, virtualization took the world by storm and that introduced multiple L2 tunneling encapsulations such as VxLAN, NVGRE and STT.&lt;/p&gt;

&lt;p&gt;In 2020, IETF also standardized GENEVE (&lt;em&gt;Generic Network Virtualization Encapsulation&lt;/em&gt;). See &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8926&quot;&gt;RFC 8926&lt;/a&gt; for details. Before you dismiss this as yet another L2 tunnel encapsulation, know that AWS already uses Geneve!&lt;/p&gt;

&lt;p&gt;Similary to VxLAN, Geneve is carried as a UDP payload and encapsulates an inner Ethernet or IP packet.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+=====+====+=====+========+================+=================+
| Eth | IP | UDP | GENEVE | (Inner) Eth/IP | (Inner) payload |
+=====+====+=====+========+================+=================+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the Geneve packet format.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver|  Opt Len  |O|C|    Rsvd.  |          Protocol Type        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Virtual Network Identifier (VNI)       |    Reserved   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
~                    Variable-Length Options                    ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To generate Geneve packets using Ostinato, we need to write a &lt;a href=&quot;https://userguide.ostinato.org/user-script/&quot;&gt;protocol builder userscript&lt;/a&gt;. Since the frame format is similar to VxLAN, I used the &lt;a href=&quot;https://srivatsp.com/ostinato/craft-vxlan-packets-using-ostinato/&quot;&gt;VxLAN userscript&lt;/a&gt; and made modifications required for Geneve, all of which was fairly straightforward.&lt;/p&gt;

&lt;p&gt;Here’s the userscript you need to use for Geneve -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;protocol.name = &quot;Geneve&quot;

protocol.protocolFrameSize = function() {
  return 8;
}

protocol.protocolFrameValue = function(index) {
    var ver = 0;	    // 0 to 3
    var opt_len = 0;	    // 0 - 63
    var control_pkt = 0;    // 0 or 1
    var critical_option = 0;// 0 or 1
    var vni = 0x749127;	    // example VNI value
    var proto = protocol.payloadProtocolId(Protocol.ProtocolIdEth);

    if (proto == 0)
        proto = 0x6558      // Assume ethernet aka Transparent Bridging

    var pfv = new Array(8);

    pfv[0] = (ver &amp;lt;&amp;lt; 6 | opt_len) &amp;amp; 0xFF;
    pfv[1] = (control_pkt &amp;lt;&amp;lt; 7 | critical_option &amp;lt;&amp;lt; 6) &amp;amp; 0xFF;
    pfv[2] = (proto &amp;gt;&amp;gt; 8) &amp;amp; 0xFF;
    pfv[3] = proto &amp;amp; 0xFF;

    pfv[4] = (vni &amp;gt;&amp;gt; 16) &amp;amp; 0xFF;
    pfv[5] = (vni &amp;gt;&amp;gt; 8) &amp;amp; 0xFF;
    pfv[6] = vni &amp;amp; 0xFF;
    pfv[7] = 0;

    return pfv;
}

protocol.protocolId = function(id_type) {
    if (id_type == Protocol.ProtocolIdTcpUdp)
        return 6081;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s verify the script by capturing our Geneve packet in Wireshark (&lt;em&gt;click to enlarge&lt;/em&gt;) -&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;https://srivatsp.com/assets/images/geneve-wireshark.png&quot; title=&quot;Ostinato generated Geneve packet in Wireshark&quot;&gt;
    &lt;img alt=&quot;Geneve packet in Wireshark&quot; src=&quot;https://srivatsp.com/assets/images/geneve-wireshark.png&quot; /&gt;
  &lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;Some limitations -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The above userscript doesn’t generate Geneve options&lt;/li&gt;
  &lt;li&gt;For the outer UDP, override the checksum field to 0 - this is to avoid an Ostinato TCP/UDP checksum bug when you have 2 (or more) IP headers in a packet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need details on how to create all the protocols headers (outer, inner) for a Geneve packet, see &lt;a href=&quot;https://srivatsp.com/ostinato/craft-vxlan-packets-using-ostinato/&quot;&gt;Crafting VxLAN packets using Ostinato&lt;/a&gt; - it’s exactly the same, just replace the VxLAN userscript with the Geneve userscript above.&lt;/p&gt;

&lt;p&gt;Or just download this &lt;a href=&quot;https://srivatsp.com/assets/others/geneve.ostm&quot;&gt;sample Ostinato stream&lt;/a&gt; and open it in Ostinato directly.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 31 Aug 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-geneve/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-geneve/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato at SharkFest 2020</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;It’s been almost a year, but I just realized that I hadn’t posted about my Ostinato talk at Sharkfest 2020 here on my blog.&lt;/p&gt;

&lt;p&gt;As a long time Ethereal/Wireshark user and Gerald Combs fan, I’ve always wanted to attend &lt;a href=&quot;https://sharkfestus.wireshark.org/&quot;&gt;Sharkfest&lt;/a&gt;, but being based in India I never had the opportunity. But COVID-19 turned all conferences virtual including the 2020 edition of SharkFest - thus, I was able to not just attend, but also present last year.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ostinato.org/docs/sharkfest2020&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-sf20v.png&quot; alt=&quot;Ostinato at Sharkfest 2020&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video and slides are already posted on the &lt;a href=&quot;https://ostinato.org/docs/sharkfest2020&quot;&gt;Ostinato website&lt;/a&gt;.&lt;/p&gt;
</description>
          <pubDate>Tue, 24 Aug 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/sharkfest-2020/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/sharkfest-2020/</guid>
        </item>
    
  
    
	<item>
          <title>How to open PCAP files in Ostinato on EVE-NG, GNS3 and CML</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;em&gt;This post is written for EVE-NG. But the procedure is exactly same for GNS3 and CML as well.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ostinato.org/eveng&quot;&gt;Ostinato (standard) for EVE-NG&lt;/a&gt; runs the Ostinato GUI controller and Drone agent within a VM accessed via VNC. This makes it very easy to use for almost all workflows.&lt;/p&gt;

&lt;p&gt;However, it introduces an extra step for the following Ostinato workflows -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Open PCAP files for edit/replay&lt;/li&gt;
  &lt;li&gt;Save Ostinato streams&lt;/li&gt;
  &lt;li&gt;Save Ostinato session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above workflows access the Ostinato VM’s local filesystem. So you need to transfer files between the host and the VM.&lt;/p&gt;

&lt;p&gt;Here are the detailed steps for transferring files.&lt;/p&gt;

&lt;h2 id=&quot;one-time-setup&quot;&gt;One time setup&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Stop the Ostinato instance, if it is running&lt;/li&gt;
  &lt;li&gt;Connect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0/mgmt&lt;/code&gt; interface of Ostinato to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Management/Cloud0&lt;/code&gt; network&lt;/li&gt;
  &lt;li&gt;Start the Ostinato instance&lt;/li&gt;
  &lt;li&gt;Open a terminal within the Ostinato VNC instance and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifconfig eth0&lt;/code&gt; to make sure it gets an IP address via DHCP&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;center&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-vm-fs-access/ost-eve-lab.png&quot; alt=&quot;Ostinato mgmt ethernet&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;transfer-files&quot;&gt;Transfer files&lt;/h2&gt;
&lt;p&gt;Check which user you are logged in as in the VM by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami&lt;/code&gt;. If the user is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ostinato&lt;/code&gt;, you are good to proceed to the transfer step, else if the user is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tc&lt;/code&gt;, it doesn’t have a password assigned and ssh/scp don’t play nice with blank passwords. Therefore -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Assign a password to user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tc&lt;/code&gt; by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;passwd&lt;/code&gt; command inside the terminal in the VNC.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you can ssh/scp to the DHCP assigned eth0 IP address using the username &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tc&lt;/code&gt; and the password that you assigned. If you are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ostinato&lt;/code&gt;, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ostinato&lt;/code&gt; as the password as well.&lt;/p&gt;

&lt;p&gt;To transfer from host to VM, &lt;strong&gt;run on the host&lt;/strong&gt; -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;scp &amp;lt;file&amp;gt; &amp;lt;user&amp;gt;@&amp;lt;ostinato-eth0-ip&amp;gt;:~
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can use the above steps to scp the pcap file to the Ostinato appliance and open it from the Ostinato GUI.&lt;/p&gt;

&lt;p&gt;To transfer from VM to host, &lt;strong&gt;run on the host&lt;/strong&gt; -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;scp &amp;lt;user&amp;gt;@&amp;lt;ostinato-eth0-ip&amp;gt;:&amp;lt;full-path-to-file&amp;gt; &amp;lt;destination-path-on-host&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can use this to save the streams/session from the Ostinato GUI and scp it to the host filesystem.&lt;/p&gt;

&lt;h2 id=&quot;persistence&quot;&gt;Persistence&lt;/h2&gt;
&lt;p&gt;Note that the Ostinato VM is designed to run out of &lt;a href=&quot;https://wiki.debian.org/ramfs&quot;&gt;ramfs&lt;/a&gt;, so any changes you make inside the VM (such as setting a password or creating files) &lt;strong&gt;won’t survive an appliance reboot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There is however one exception.&lt;/p&gt;

&lt;p&gt;Any files in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/ostinato/&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/tc&lt;/code&gt;)  can be made persistent by doing a clean shutdown of the appliance from inside the VM -&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Exit&lt;/code&gt; from the menu at the bottom of the window&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-vm-fs-access/tc-clean-exit.png&quot; alt=&quot;Ostinato VM clean shutdown&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Backup&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Backup options&lt;/code&gt; in the &lt;em&gt;TC Exit Options&lt;/em&gt; window and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OK&lt;/code&gt; &lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-vm-fs-access/tc-shut-backup.png&quot; alt=&quot;Ostinato VM backup files&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you do a clean exit as shown above, files in your home directory will be saved and restored across a reboot. Stopping the Ostinato node from the EVE-NG lab is not a clean exit and therefore your files won’t be pesistent.&lt;/p&gt;

&lt;p&gt;For more Ostinato related content, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Wed, 18 Aug 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ost-vm-fs-acess/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ost-vm-fs-acess/</guid>
        </item>
    
  
    
	<item>
          <title>Test SDWAN using application traffic</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Did you know that you could generate application traffic using &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; to test your SDWAN and application aware routing deployments?&lt;/p&gt;

&lt;p&gt;In this post, I’m going to show you how.&lt;/p&gt;

&lt;p class=&quot;notice--warning&quot;&gt;Note that the application traffic generated by Ostinato will be &lt;strong&gt;stateless, synthetic traffic&lt;/strong&gt; for the purpose of allowing your SDWAN edge nodes to detect the application and trigger the right policy — so you can verify your policies.&lt;br /&gt;&lt;br /&gt;
Ostinato is not stateful, does not support TCP connections and cannot generate fake traffic to websites to increase ad impressions etc.&lt;/p&gt;

&lt;p&gt;Let’s generate Facebook traffic -&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Create a new stream&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/common/new-stream.png&quot; alt=&quot;create new stream&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Change packet size to 1500&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/common/pkt-size-1500.png&quot; alt=&quot;set packet size&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Select protocols - Mac | Untagged | Ethernet | IPv4 | TCP | Text | Pattern&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/common/protocols-tcp-text.png&quot; alt=&quot;set application protocols&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Go to Protocol Data tab, click on &lt;strong&gt;Internet Protocol ver 4&lt;/strong&gt; (IPv4) section and set source and destination IP addresses&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/common/ip-address.png&quot; alt=&quot;set ip address&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Go to &lt;strong&gt;Transmission Control Protocol&lt;/strong&gt; (TCP) section, override the source port and enter any value between 1024 and 65535 (this value MUST be unique for each application flow)&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/common/tcp-src-port.png&quot; alt=&quot;set source tcp port&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Go to the &lt;strong&gt;Text Protocol&lt;/strong&gt; section, change line-ending to CRLF (as required by the HTTP standard) and enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET / HTTP/1.1&amp;lt;Enter&amp;gt;Host: facebook.com&amp;lt;Enter&amp;gt;&amp;lt;Enter&amp;gt;&amp;lt;Enter&amp;gt;&lt;/code&gt;. Note the multiple enters at the end to insert blank lines - those are REQUIRED - HTTP requests end with two CRLF (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0D 0A 0D 0A&lt;/code&gt;) - you can verify this in the Packet view tab&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/app-traffic/http-request.png&quot; alt=&quot;set http request&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Go to stream control and set the rates etc.&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/app-traffic/set-stream-rates.png&quot; alt=&quot;set stream rates&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To verify if the traffic is generated correctly, I used &lt;a href=&quot;https://www.ntop.org/products/traffic-analysis/ntop/&quot;&gt;ntopng&lt;/a&gt; which uses &lt;a href=&quot;https://www.ntop.org/products/deep-packet-inspection/ndpi/&quot;&gt;nDPI&lt;/a&gt; - an open-source deep packet inspection library&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/app-traffic/ntop-facebook.png&quot; alt=&quot;ntop detects facebook traffic&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Want to generate YouTube traffic instead? Just change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Host: facebook.com&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Host: youtube.com&lt;/code&gt; or any other application as required in the &lt;em&gt;Text Protocol&lt;/em&gt; configuration. Remember if you are generating traffic for multiple applications, &lt;strong&gt;you MUST give each application a different TCP source port value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/app-traffic/ntop-facebook-youtube.png&quot; alt=&quot;ntop detects application traffic&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Not working for you? Double-check the following -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make sure source/destination mac are resolved; otherwise set mode to &lt;em&gt;Fixed&lt;/em&gt; and set them manually&lt;/li&gt;
  &lt;li&gt;Make sure the HTTP text ends with at least two CRLF (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0D 0A 0D 0A&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;If sending traffic for multiple applications/protocols, make sure you change the source TCP port numbers so that each flow is unique&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve used ntopng/nDPI here for application detection. This procedure should work with other application detection libraries and devices as well. If it doesn’t, let me know and we can try to figure it out together.&lt;/p&gt;

</description>
          <pubDate>Tue, 10 Aug 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/sdwan-application-traffic/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/sdwan-application-traffic/</guid>
        </item>
    
  
    
	<item>
          <title>Staircase style traffic with Ostinato</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A few weeks ago, &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; user Peter asked me -&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I basically want to send traffic at different traffic % load for a specific interval of time. &lt;strong&gt;10 seconds 10% 1G, 10 seconds 20% 1G, through 10 seconds 90% 1G and then back down to 10% and repeat&lt;/strong&gt;. Is there a way to do that through the [Ostinato] GUI?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I sent him instructions to do so and figured it might be useful for others too, so posting here as well -&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note that although Peter didn’t need to generate 100% 1G traffic, it is easy to extend the below instructions to do that.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s first visualize this staircase style traffic requirement -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-staircase-traffic-req.png&quot; alt=&quot;Staircase Traffic requirement&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that Ostinato stream configuration takes input as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Number of Packets&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Duration in seconds&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Packets/sec&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;% line rate&lt;/code&gt;, so we need to do some conversion first. Here are the formulae for the same -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PacketsPerSec = TargetBpsRate/((PktSize+20)*8)
NumberOfPackets = PacketsPerSec * DurationInSec
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;20 bytes is the per-packet ethernet line rate overhead (preamble, start of frame and inter packet gap)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ll assume for now that packet size doesn’t matter and go with 1500 byte packets (more on this later).&lt;/p&gt;

&lt;p&gt;Using the above formulae, we calculate Packet Rate and Number of Packets for each of the 9 streams -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-staircase-traffic-rates.png&quot; alt=&quot;Staircase Traffic Stream Rates&quot; /&gt;&lt;/p&gt;

&lt;p&gt;See the &lt;a href=&quot;https://docs.google.com/spreadsheets/d/1jMyyyUKbzUd3gqAn2CMbYi74xTruGjBXPdjhBBTgvv8/edit?usp=sharing&quot;&gt;table on Google Sheets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next steps -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Create stream #1 in the GUI and configure the protocols and fields as desired.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Configure packet size as 1500&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-staircase-traffic-pktsize.png&quot; alt=&quot;Staircase Traffic Packet Size&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Configure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Packet Rate&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Number of Packets&lt;/code&gt; from the table above corresponding to stream #1&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-staircase-traffic-pps.png&quot; alt=&quot;Staircase Traffic Rate and Duration&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Duplicate the stream - enter count as 8 (for a total of 9 streams)&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-staircase-traffic-dup-streams.gif&quot; alt=&quot;Staircase Traffic Stream Duplication&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Edit the remaining 8 streams and change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;packet rate&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;number of packets&lt;/code&gt; as per the above table&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Ensure the first 8 streams are set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Goto Next&lt;/code&gt; and the last one to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Goto First&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;https://srivatsp.com/assets/images/ost-staircase-traffic-streams-next.png&quot; alt=&quot;Staircase Traffic Stream Next&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Finally, hit “Apply” followed by “Start Transmit”&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason I picked 1500 as the packet size as opposed to 64 (default packet size for Ostinato streams) is that Ostinato cannot do 1Gbps for 64 byte packets without the Turbo add-on.&lt;/p&gt;

&lt;p&gt;To get line rate for small packet sizes like 64 bytes, you can use the &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;Ostinato Turbo add-on&lt;/a&gt; which provides wire speed 10/25/40Gbps&lt;/p&gt;

&lt;p&gt;If you have any questions, post in the comments below.&lt;/p&gt;
</description>
          <pubDate>Tue, 03 Aug 2021 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/staircase-traffic/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/staircase-traffic/</guid>
        </item>
    
  
    
	<item>
          <title>A deeper understanding of the OSI 7-layer model</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;em&gt;In the previous post, I talked to &lt;a href=&quot;https://srivatsp.com/ostinato/graham-cassells/&quot;&gt;Graham Cassells&lt;/a&gt; - a Cisco Networking Academy teacher in Australia, who is using Ostinato in his classes. I also talked to his students at &lt;a href=&quot;https://www.ltc.act.edu.au/&quot;&gt;Lake Tuggeranong College&lt;/a&gt; about their course, the tools and the topics that interest them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a bright set of kids! They’re interested in IT, Networking, cyber-security, and they’re keen to understand how hacking and penetration testing works.&lt;/p&gt;

&lt;p&gt;The students generally use Packet tracer, Wireshark and Ostinato on both Windows 10 and Kali Linux for network related studies. For security practice, they use a &lt;strong&gt;cyber range&lt;/strong&gt; - a Lego city with hackable trains and planes.&lt;/p&gt;

&lt;figure class=&quot;&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ltc-graham-class.jpg&quot; alt=&quot;Graham&amp;#39;s networking class at LTC&quot; /&gt;&lt;figcaption&gt;
      Cisco networking academy students at LTC

    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;One of their recent study exercises was to run a normal ping, capture and study the packet in Wireshark, then use Ostinato to replicate and spoof the same packet to trick or fool a victim. They loved this exercise.&lt;/p&gt;

&lt;p&gt;Using Ostinato helped the students visualize encapsulation and the OSI model layers in practice for a deeper understanding of networks and packets. They weren’t just learning the theory any more, they were creating these packets and running it through the system to see how it fared.&lt;/p&gt;

&lt;p&gt;Christopher from the class said, “Ostinato was great for understanding more on how a packet is structured and sent across a network”. Connor, another student, said the exercise provided a practical understanding of Mac and IP addresses and enhanced their classroom theory training. Since Ostinato works great with other tools like Wireshark - so they’d definitely recommend it to their friends and other students.&lt;/p&gt;

&lt;p&gt;Several students found it fascinating that when used for pen-testing, Ostinato-created packets couldn’t be told apart from normal packets, if done right. The digital fingerprint of the Ostinato created packet was identical to the one they were spoofing.&lt;/p&gt;

&lt;p&gt;I asked them about what they found lacking with Ostinato and students basically wanted to use it better - they asked for more tutorials and guides to fully utilitize all the capabilities of the tool (&lt;em&gt;More tutorials, is something I hear very often and will try to provide!&lt;/em&gt;). There was also a specific feedback about the Ostinato stream configuration dialog not showing a scroll bar and so they didn’t realize there were more options they could select (&lt;em&gt;Will fix that in the next version!&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thank you students for talking to me!&lt;/em&gt;&lt;/p&gt;
</description>
          <pubDate>Tue, 08 Dec 2020 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ltc-students/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ltc-students/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato improves networking students&apos; understanding and skill level&amp;#58; Graham Cassells</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/graham-cassells.jpg&quot; alt=&quot;Graham Cassells&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Graham Cassells is a CCNA and teacher of networking and cyber security at high schools and TAFE (vocational training for adults) for the last 20 years. He currently teaches the &lt;a href=&quot;https://www.netacad.com/&quot;&gt;Cisco Networking Academy&lt;/a&gt; program at Canberra’s Lake Tuggeranong College in Australia.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;He has been using &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; in his classes and was gracious enough to answer my questions via email. I asked him about the Cisco Networking Academy, how he uses Ostinato and his annoyances and wishlist for Ostinato.&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;cisco-networking-academy-program&quot;&gt;Cisco Networking Academy program&lt;/h1&gt;

&lt;h3 id=&quot;what-is-the-cisco-networking-academy-program&quot;&gt;What is the Cisco Networking Academy program?&lt;/h3&gt;
&lt;p&gt;The Cisco Networking Academy is an online program run around the world by Cisco for skills towards attaining the CCNA.&lt;/p&gt;

&lt;h3 id=&quot;how-is-the-program-structured-in-terms-of-duration-and-coursework&quot;&gt;How is the program structured in terms of duration and coursework?&lt;/h3&gt;
&lt;p&gt;There are lots of courses under the academy. There are 3 networking units that comprise of around 10-15 modules - online study resources, short interactive quizzes and assessments, around 4 hours of study per week over 16 weeks. It takes around 2 years of part time study to complete the program.&lt;/p&gt;

&lt;h3 id=&quot;how-many-students-do-you-typically-have-in-a-batch&quot;&gt;How many students do you typically have in a batch?&lt;/h3&gt;
&lt;p&gt;We run classes of around 20 each year in our senior high school (college) years 11 &amp;amp; 12 for 16-18 year olds – for free. Our industry contacts are very envious of our young students who get the opportunity to study Cisco at school.&lt;/p&gt;

&lt;h3 id=&quot;which-courses-in-the-program-do-you-teach&quot;&gt;Which courses in the program do you teach?&lt;/h3&gt;
&lt;p&gt;I teach the Routing and Switching course - it is integrated in our ACT department of Education IT courses along with cyber security topics to enhance the practical side of the theory.&lt;/p&gt;

&lt;h3 id=&quot;what-networking-technologies-does-the-course-include&quot;&gt;What networking technologies does the course include?&lt;/h3&gt;
&lt;p&gt;The OSI encapsulation model, IPv4 &amp;amp; IPv6 subnetting, setting up switches, routers, building simple networks, setting up security including firewalls such as ACLs, and using fault finding techniques.&lt;/p&gt;

&lt;h3 id=&quot;what-equipment-tools-and-software-do-you-use&quot;&gt;What equipment, tools and software do you use?&lt;/h3&gt;
&lt;p&gt;We have Cisco 2960 switches and Cisco 1941 routers, wireless routers, PCs, Raspberry Pis and testing equipment. Our software includes Packet Tracer, Wireshark, Putty and Ostinato under various platforms such as Linux and Windows.&lt;/p&gt;

&lt;figure class=&quot;&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ltc-graham-class.jpg&quot; alt=&quot;Graham&amp;#39;s networking class at LTC&quot; /&gt;&lt;figcaption&gt;
      Graham’s networking class at LTC

    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h1 id=&quot;ostinatos-role-in-networking-education&quot;&gt;Ostinato’s role in networking education&lt;/h1&gt;

&lt;h3 id=&quot;how-did-you-discover-ostinato&quot;&gt;How did you discover Ostinato?&lt;/h3&gt;
&lt;p&gt;We looked for packet injection and creation tools and it was listed everywhere as a good tool to start with.&lt;/p&gt;

&lt;h3 id=&quot;were-there-any-similar-tools-you-also-evaluated&quot;&gt;Were there any similar tools you also evaluated?&lt;/h3&gt;
&lt;p&gt;We looked at Hping and Scapy.&lt;/p&gt;

&lt;h3 id=&quot;why-did-you-select-ostinato-over-other-tools&quot;&gt;Why did you select Ostinato over other tools?&lt;/h3&gt;
&lt;p&gt;The time needed to learn the coding didn’t work well and Ostinato’s GUI and price suited the college well.&lt;/p&gt;

&lt;h3 id=&quot;what-do-you-use-ostinato-for-and-how&quot;&gt;What do you use Ostinato for and how?&lt;/h3&gt;
&lt;p&gt;We set up a practical assessment task (the students work in pairs) - make a simple client to client network and Ping and Wireshark the result. Then, create a packet that spoofs the sender and compare the results to get a deeper understanding of encapsulation and digital forensic analysis.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;The objective is understanding more than how packet data looks, it&apos;s about how it works at the levels in the OSI model&lt;/u&gt;.&lt;/p&gt;

&lt;h3 id=&quot;as-a-teacher-what-do-you-like-most-about-ostinato&quot;&gt;As a teacher, what do you like most about Ostinato?&lt;/h3&gt;
&lt;p&gt;The quick and simple way to create injection packets using the GUI, and the way it links directly with Wireshark for analysis.&lt;/p&gt;

&lt;h3 id=&quot;how-has-ostinato-impacted-learning-outcomes&quot;&gt;How has Ostinato impacted learning outcomes?&lt;/h3&gt;
&lt;p&gt;The students enjoy the application and it consolidates the abstract theories that need understanding in the application of networking technologies analysis and use.&lt;/p&gt;

&lt;h3 id=&quot;would-you-recommend-ostinato-to-other-educators&quot;&gt;Would you recommend Ostinato to other educators?&lt;/h3&gt;
&lt;p&gt;I would definitely recommend Ostinato to all teachers who teach networking - it brings the theory out and allows the practical application that improves students’ understanding and skill level.&lt;/p&gt;

&lt;h1 id=&quot;ostinato-feedback&quot;&gt;Ostinato feedback&lt;/h1&gt;

&lt;h3 id=&quot;what-in-ostinato-irritates-you-the-most-and-youd-like-fixed&quot;&gt;What in Ostinato irritates you the most and you’d like fixed?&lt;/h3&gt;
&lt;p&gt;None really. If you follow the basic use videos on Youtube it is easy to set up and use. The most annoying thing is not knowing how to make packets for more services to test - it takes time, I guess.&lt;/p&gt;

&lt;h3 id=&quot;what-one-thing-if-added-to-ostinato-would-make-it-more-useful-for-you&quot;&gt;What one thing, if added to Ostinato, would make it more useful for you?&lt;/h3&gt;
&lt;p&gt;More teaching resources to show more ways it can be used - maybe tutorials, Youtube vidoes of simulating applications.&lt;/p&gt;

&lt;h3 id=&quot;what-do-your-students-think-about-ostinato&quot;&gt;What do your students think about Ostinato?&lt;/h3&gt;
&lt;p&gt;They have commented that they liked it, and the energy they showed in class was higher than normal as they worked to create a forensic packet comparison.&lt;/p&gt;

&lt;h3 id=&quot;anything-else-you-would-like-to-say&quot;&gt;Anything else you would like to say?&lt;/h3&gt;
&lt;p&gt;Please keep developing it, and make some tutorial videos to show more complex use, e.g. how to make a simple web page payload, email, DHCP, DNS, FTP packets would be great and would cover a lot in terms of our lack of understanding in these technologies.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next up - What Graham’s students think about Ostinato.&lt;/em&gt;&lt;/p&gt;
</description>
          <pubDate>Wed, 18 Nov 2020 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/graham-cassells/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/graham-cassells/</guid>
        </item>
    
  
    
	<item>
          <title>A bigger nudge to check the logs</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;In Ostinato version 1.0, I added a Logs window to the UI so that errors/warnings could be notified to the user. Since most often the logs window is hidden behind a tab, I added a blinking &lt;img src=&quot;https://srivatsp.com/assets/images/ost-error-anim-icon.png&quot; alt=&quot;error&quot; /&gt; icon to the tab whenever there was an error or warning.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-error-anim-icon-zoom.png&quot; alt=&quot;Easy to miss icon&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But, there at the bottom there - that’s easy to miss.&lt;/p&gt;

&lt;p&gt;With the upcoming Ostinato 1.1, you won’t -&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/m9fI7_7NkfM?rel=0&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;It may seem like a small thing. But small things matter. They are not inconsequential. Not to someone who misses that tiny blinking icon and wastes time wondering why something is not working as expected.&lt;/p&gt;

&lt;p&gt;This is one of the last bits that was planned for Ostinato v1.1 - I will now move on to doing pre-release testing followed by making the release and building the packages.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;
</description>
          <pubDate>Tue, 02 Jun 2020 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-error-anim/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-error-anim/</guid>
        </item>
    
  
    
	<item>
          <title>Test NAT using Ostinato</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Recently, &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; customer &lt;em&gt;Javor&lt;/em&gt; reached out to me and wanted advice on how to configure traffic stream(s) to test 2 million NAT sessions.&lt;/p&gt;

&lt;p&gt;A NAT session is based on the 5-tuple &lt;em&gt;(SrcIP, DstIP, Protocol, SrcPort, DstPort)&lt;/em&gt;. One NAT session is created per unique 5-tuple.&lt;/p&gt;

&lt;p&gt;His first attempt was to configure the Src/Dst IP as random but he ran into a &lt;a href=&quot;https://github.com/pstavirs/ostinato/issues/51&quot;&gt;known Ostinato bug&lt;/a&gt; that causes incorrect TCP/UDP checksum. I suggested he workaround the checksum problem by overriding it to 0 (0 checksum value implies no checksum), but his test required that checksums not be 0. So he tried to configure incremental Src/Dst IP alongwith incremental Src/Dst Port and ran into a different problem. In his own words,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I find Ostinato for a great product.&lt;/p&gt;

  &lt;p&gt;But now I’m fighting with Ostinato to test CGNAT performance of one router and trying to create some stream that can generate the following packets with valid checksum mandatory.&lt;/p&gt;

  &lt;p&gt;So when I create a new stream with:&lt;br /&gt;
SRC IP - Increment - 10.0.0.0/16 - 1000 hosts&lt;br /&gt;
DST IP - Increment - 16 hosts&lt;br /&gt;
SRC Ports - incremental - very important for this test - 60000&lt;br /&gt;
DST Ports - incremental - 100&lt;br /&gt;&lt;/p&gt;

  &lt;p&gt;In fact the stream works but not in way that I expect.&lt;/p&gt;

  &lt;p&gt;The stream just generated sequentially flows one by one like this for example:&lt;br /&gt;
src-ip_1, src port 30001 -&amp;gt; dst-ip_1, port 1001&lt;br /&gt;
src-ip_2, src port 30002 -&amp;gt; dst-ip_2, port 1002&lt;br /&gt;
src-ip_3, src port 30003 -&amp;gt; dst-ip_3, port 1003&lt;br /&gt;&lt;/p&gt;

  &lt;p&gt;But I don’t want this. I would like to achieve the following way of order and incrementation:
src_ip1, src port 30001 -&amp;gt;  dst-ip_1, port 1001&lt;br /&gt;
src_ip1, src port 30002 -&amp;gt;  dst-ip_1, port 1002&lt;br /&gt;
src_ip1, src port 30003  -&amp;gt; dst-ip_1, port 1003&lt;br /&gt;
src_ip1, src port 30004  -&amp;gt; dst-ip_1, port 1004&lt;br /&gt;
src_ip1, src port 30005  -&amp;gt; dst-ip_1, port 1005&lt;br /&gt;
……&lt;/p&gt;

  &lt;p&gt;So I would like the stream to generate the packets in order to start first from src-ip and incrementally passthrough over all src-ports range ( for example range of 5000 ports ) and then to go to the next src-ip then to next src-ip and so on….&lt;/p&gt;

  &lt;p&gt;Final goal is to utilize the router device with maximum unique sessions but not only unique per IP but each IP to generate more sessions. I’m trying to utilize ~2mln sessions.&lt;/p&gt;

  &lt;p&gt;As workaround I have tried to generate a lot of streams with fixed src/dst hosts with incremental range of src ports and it works but for 2mln sessions I need to generate a lot of streams ~ 240 streams in my case which is very very difficult and a lot of work - Copy/duplicate/change src-dst host and so on.&lt;/p&gt;

  &lt;p&gt;So I wonder if there is any easier way to achieve this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essentially the problem is that when you have multiple variable fields configured on a stream, each variable field will increment with every packet.&lt;/p&gt;

&lt;p&gt;What Javor wants is a way to increment one variable field every X number of packets (where X &amp;gt; 1) while another variable field increments with every packet.&lt;/p&gt;

&lt;p&gt;I told him that this was currently not possible and support may be added in a future release, but the problem, for some reason, refused to leave my head. A couple of days of background processing later, I had an insight. This is possible to do now albeit with some restrictions. Restrictions that I think Javor can live with.&lt;/p&gt;

&lt;p&gt;Here’s how.&lt;/p&gt;

&lt;p&gt;Let’s start with the objective of 2 million NAT sessions. If we iterate over the entire SrcPort range (0 – 65535), we need 2,000,000/65536 = ~ 30.5 SrcIP; let’s round it up to 32 SrcIP.&lt;/p&gt;

&lt;p&gt;So, we create two variable fields - one for SrcPort and another for SrcIP.&lt;/p&gt;

&lt;p&gt;The SrcPort variable field configuration is straight forward -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Increment SrcPort from 0 to 65535 step 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-nat-vfield-src-port.png&quot; alt=&quot;SrcPort variable field&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(If you are using Ostinato v1.0 or earlier, you will not be able to enter count as 65536 due to a bug. Once you read this post in full, you will find a workaround for it)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The SrcIP is trickier. We’ll go slow. The most critical part is that the SrcIP should be incremented by 1 every 65536 packets.&lt;/p&gt;

&lt;p&gt;Variable field mask to the rescue!&lt;/p&gt;

&lt;p&gt;Originally, the variable field mask was implemented to allow increments in bit fields smaller than a byte without affecting adjacent bit fields. But it can do more—we can use mask to achieve what we want in this case.&lt;/p&gt;

&lt;p&gt;From the &lt;a href=&quot;https://userguide.ostinato.org/stream-config/#variable-fields&quot;&gt;Ostinato user guide&lt;/a&gt; -&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The computation used for varying fields is as follows -&lt;/p&gt;
  &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;new = (old AND ~mask) OR ((value op n*step) AND mask)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;where&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;old is 8/16/32-bit value (depending on counter type) starting at offset bytes from start of protocol&lt;/li&gt;
    &lt;li&gt;op is based on the mode - increment/decrement/random&lt;/li&gt;
    &lt;li&gt;n varies from 0 to (count-1) incrementing by 1 every packet&lt;/li&gt;
    &lt;li&gt;new is the value that will replace old&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a IP packet, the Src IP is 4 bytes (offsets 12 to 15) and Dst IP is 4 bytes (offsets 16 to 19). Let’s define a &lt;em&gt;custom&lt;/em&gt; field for IPv4 as follows -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Field Custom&lt;/li&gt;
  &lt;li&gt;Type Counter32&lt;/li&gt;
  &lt;li&gt;Offset 14&lt;/li&gt;
  &lt;li&gt;Mask 0xFFFF0000&lt;/li&gt;
  &lt;li&gt;Mode Increment&lt;/li&gt;
  &lt;li&gt;Step 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-nat-vfield-src-ip-partial.png&quot; alt=&quot;SrcIP variable field (partial config)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s not bother about &lt;em&gt;Value&lt;/em&gt; and &lt;em&gt;Count&lt;/em&gt; for now. We’ll come to those. For now, let’s look at this visually. Given an IP frame with SrcIP as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s1.s2.s3.s4&lt;/code&gt; and DstIP as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;d1.d2.d3.d4&lt;/code&gt; -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Offset      12                  16                  20
         ---+----+----+----+----+----+----+----+----+---
IP Frame    | s1 | s2 | s3 | s4 | d1 | d2 | d3 | d4 |  
         ---+----+----+----+----+----+----+----+----+---
Mask                  | FF | FF | 00 | 00 |
         ---+----+----+----+----+----+----+----+----+---
N*Step                | 00 | 00 | 00 | 01 | &amp;lt;== increments every packet
         ---+----+----+----+----+----+----+----+----+---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our mask covers the last 2 bytes of SrcIP and first 2 bytes of DstIP. But the mask makes sure that the DstIP is not modified. For every packet, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;N*Step&lt;/code&gt; value will be incremented by 1. &lt;strong&gt;The first 65536 packets will have this value increment from 0 to 65535 (0xFFFF) i.e. only the last 2 bytes are changed across these many packets, but because the mask blocks off change in these 2 bytes, they are not reflected in the packet&lt;/strong&gt;. For the 65537th packet, this value will become 0x10000 (65536) which means the LSB of the last byte of SrcIP will get incremented. Subsequent packets will again change only the lower 2 bytes (which has no impact because of the mask) while the upper two bytes have the constant value 0x0001. After another 65536 iterations, the upper 2 bytes will be incremented by 1 i.e. to 0x0002 and so on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Effectively, we are ensuring that SrcIP gets incremented every 65536 iterations!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you work with binary and hexadecimal numbers regularly, this should make sense. If not, re-read it a couple of times and it will— &lt;em&gt;trust me!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The insightful reader would have noticed that this will work only with powers of 2 (e.g. 65536 = 2^16). &lt;em&gt;Yes, that’s the limitation that I talked about at the start!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s come to the remaining fields.&lt;/p&gt;

&lt;p&gt;First &lt;em&gt;count&lt;/em&gt;. We want 32 SrcIP across 65536 SrcPort, so the count should be 32*65536 = 2097152&lt;/p&gt;

&lt;p&gt;Finally &lt;em&gt;value&lt;/em&gt;. This is the starting value of SrcIP. But, only the last 2 bytes of it followed by first 2 bytes of DstIP (that we don’t want to modify). Let’s say you want the starting value of SrcIP to be 10.2.24.100 (0x0A021864), we take the last 2 bytes 0x1864 as the first 2 bytes of the value and the last 2 bytes as 0x0000 i.e. 0x18640000, convert it to decimal (409206784) and enter that in the value field -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-nat-vfield-src-ip-full.png&quot; alt=&quot;SrcIP variable field (full config)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s it, we can now test 2 million NAT sessions using a single Ostinato stream! And depending on the NAT table size of the DUT, you can even simulate a NAT table full condition!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not so fast, though!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There’s another piece of coincidence in this example. The SrcPort variable field count 65536 is exactly equal to 2^16—the range of increments that we mask off by configuring the last 2 bytes (16 bits) of the mask as 0.&lt;/p&gt;

&lt;p&gt;What if we want a different range to vary for SrcPort. It would still work &lt;em&gt;as long as the count is a power of 2&lt;/em&gt;. e.g. let’s say we want to vary across 16384 (16K) ports instead of 65536 (64K). All we have to do is change SrcIpStep such that SrcIpStep*SrcPortCount = the masked off portion (2^16 = 64K). In other words, Src IP Step = 64K/SrcPortCount = 4 for this example. This is useful when some NAT systems limit the max NAT session per host to a smaller number.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While writing this blog post, I &lt;a href=&quot;https://github.com/pstavirs/ostinato/issues/301&quot;&gt;found a bug&lt;/a&gt; that the count field in the Variable Fields GUI does not allow to enter 65536 for a 16 bit field such as the UDP source port. This bug is present in all versions uptil 1.0. If you are using one of these versions, instead of 32 SrcIp * 65536 Src Ports, you can use 64 SrcIp * 32768 Src ports to achieve 2 million NAT sessions—it’s still a single stream.&lt;/em&gt;&lt;/p&gt;

</description>
          <pubDate>Tue, 05 May 2020 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-nat-variable-fields/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-nat-variable-fields/</guid>
        </item>
    
  
    
	<item>
          <title>Qt model view and cut copy paste</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;I’ve spent the last few weeks implementing &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-copy-paste/&quot;&gt;cut-copy-paste in Ostinato&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While &lt;a href=&quot;https://www.qt.io&quot;&gt;Qt&lt;/a&gt; (the cross-platform library that Ostinato uses for the UI) has great documentation, how to implement cut-copy-paste was not so obvious. Especially with &lt;a href=&quot;https://doc.qt.io/qt-5/model-view-programming.html&quot;&gt;Qt Model-Views&lt;/a&gt; which is used extensively by Ostinato. I hope this post will help you, if you are looking to do something similar.&lt;/p&gt;

&lt;p&gt;The first thing to know is that the implementation of &lt;em&gt;drag and drop&lt;/em&gt; and &lt;em&gt;clipboard actions (cut, copy, paste)&lt;/em&gt; are very similar. You should first read the Qt documentation for &lt;a href=&quot;https://doc.qt.io/qt-5/dnd.html&quot;&gt;Drag and Drop&lt;/a&gt; and &lt;a href=&quot;https://doc.qt.io/qt-5/model-view-programming.html#using-drag-and-drop-with-item-views&quot;&gt;Using Drag and Drop with Item Views&lt;/a&gt; before proceeding further. Also read about &lt;a href=&quot;https://doc.qt.io/qt-5/qclipboard.html&quot;&gt;QClipboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Go ahead, I’ll wait.&lt;/p&gt;

&lt;p&gt;Need some convincing? The reason why that documentation is important to read is because it describes all the concepts we need and also mentions clipboard actions briefly without going into details. The clipboard action implementation details are what we will get into here.&lt;/p&gt;

&lt;h1 id=&quot;model-support-for-cut-copy-paste&quot;&gt;Model support for cut, copy, paste&lt;/h1&gt;
&lt;p&gt;This should be fairly clear to you by now (&lt;em&gt;you did read the drag and drop documentation linked above didn’t you?&lt;/em&gt;). Here’s a short recap.&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;&lt;strong&gt;Note&lt;/strong&gt;: Most of Ostinato’s views (that support copy-paste) are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTableView&lt;/code&gt;, so that’s what we’ll talk about here. The concepts apply to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QListView&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTreeView&lt;/code&gt; also but we won’t discuss those.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QAbstractItemModel&lt;/code&gt; methods relevant for &lt;em&gt;copy&lt;/em&gt; are -&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;QStringList&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mimeTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;QMimeData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QModelIndexList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To &lt;em&gt;paste&lt;/em&gt; the data into the model, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QAbstractItemModel&lt;/code&gt; methods used are -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;canDropMimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Qt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DropAction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QModelIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dropMimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Qt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DropAction&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QModelIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Since &lt;em&gt;cut&lt;/em&gt; is just a &lt;em&gt;copy&lt;/em&gt; plus &lt;em&gt;delete&lt;/em&gt;, to support the latter the model needs to implement -&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removeRows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QModelIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QModelIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Ostinato uses views with a selection behaviour of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QAbstractItemView::SelectRows&lt;/code&gt;, so a delete means deleting a row, rather than clearing the contents of a single cell.&lt;/p&gt;

&lt;p&gt;Models which don’t implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;removeRows()&lt;/code&gt; won’t support a &lt;em&gt;cut&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QAbstractItemModel&lt;/code&gt; default implementation for the above methods uses a application specific mime-type of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application/x-qabstractitemmodeldatalist&lt;/code&gt; which uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QDataStream&lt;/code&gt; to encode row, column and data for each selected cell. This mime data can be copied to the clipboard and pasted back using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dropMimeData()&lt;/code&gt; which decodes it back into row, column and data to update the model.&lt;/p&gt;

&lt;p&gt;This should work like a charm as long as you export/import all the data from your model via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setData()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In case of Ostinato, the Stream/DeviceGroup objects have a large number of fields in a complex hierarchy that cannot be displayed in a simple table, so the model only exposes some of them.&lt;/p&gt;

&lt;p&gt;This meant that I had to re-implement the above functions in my models with my own custom mime-type and encoding/decoding. I will not get into the details of that here as this is really application specific.&lt;/p&gt;

&lt;h1 id=&quot;create-the-cut-copy-paste-actions&quot;&gt;Create the cut, copy, paste actions&lt;/h1&gt;
&lt;p&gt;The Qt class for menu items (application menu or context menu) is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QAction&lt;/code&gt;. We want a single set of these actions that can be used both in the application menu bar under the top level &lt;em&gt;Edit&lt;/em&gt; menu and also in the right-click context menu for each top level window/widget in our application.&lt;/p&gt;

&lt;p&gt;I created a singleton &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClipboardHelper&lt;/code&gt; class, added the actions as private members and exposed them via a public &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actions()&lt;/code&gt; method -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClipboardHelper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Q_OBJECT&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;QList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slots&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionTriggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;private:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;Cut&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setObjectName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QStringLiteral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;actionCut&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setIcon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QIcon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromUtf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;:/icons/cut.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cop&amp;amp;y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setObjectName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QStringLiteral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;actionCopy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setIcon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QIcon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromUtf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;:/icons/copy.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;Paste&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setObjectName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QStringLiteral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;actionPaste&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setIcon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QIcon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromUtf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;:/icons/paste.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SIGNAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;triggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SLOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionTriggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SIGNAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;triggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SLOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionTriggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SIGNAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;triggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SLOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionTriggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;QList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QAction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Note that I have used a single slot for all 3 actions - this is because their implemenation is very similar, so we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QObject::sender()&lt;/code&gt; to identify which action triggered the slot. The key point of &lt;em&gt;executing&lt;/em&gt; the action is that we find the widget that has the current focus, check if that widget has a slot corresponding to the action and if so, we invoke that slot. This is all done using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QMetaObject&lt;/code&gt; magic -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionTriggered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qApp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// single slot to handle cut/copy/paste - find which action was triggered&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objectName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toLower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metaObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexOfSlot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qPrintable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// slot not found in focus widget corresponding to action&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QMetaObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invokeMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qPrintable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Qt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We now add these actions to the main window menubar’s &lt;em&gt;Edit&lt;/em&gt; menu -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clipboardHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMainWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;clipboardHelper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;menuEdit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We also add these actions to the context menu of each window/widget as required -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;streamList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;deviceGroupList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We now have our actions available in the UI, which when clicked trigger the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClipboardHelper::actionTriggered()&lt;/code&gt; slot which in turn invokes the corresponding slot in the &lt;em&gt;item view&lt;/em&gt; widget which will call into the model to execute the action.&lt;/p&gt;

&lt;p&gt;What is our &lt;em&gt;item view&lt;/em&gt; class? In Ostinato’s case it is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTableView&lt;/code&gt;. But if you look at the Qt documentation for this class or it’s ancestor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QAbstractItemView&lt;/code&gt; class, you’ll see that they have no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cut()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;paste()&lt;/code&gt; slots!&lt;/p&gt;

&lt;p&gt;We will create a new subclass by inheriting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTableView&lt;/code&gt; and add these slots. All the windows/widgets that used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTableView&lt;/code&gt; will now use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XTableView&lt;/code&gt; instead -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;XTableView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QTableView&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Q_OBJECT&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QTableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slots&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paste&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To implement the copy slot, we call the model’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mimeData()&lt;/code&gt; with the current selection. But before we do that we sort the selection because views allow arbitrary selection using Ctrl/Shift modifiers. The data returned from the model is handed over to the global clipboard object -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QModelIndexList&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selectionModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectedIndexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;QMimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;qApp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clipboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setMimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To paste, we retrieve the item from the clipboard, verify if the model can accept the mime type by calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canDropMimeData()&lt;/code&gt; before calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dropMimeData()&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paste&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qApp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clipboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// If no selection, insert at the end&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectionModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasSelection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selectionModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selectionModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rowCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columnCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canDropMimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Qt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CopyAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QModelIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dropMimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Qt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CopyAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QModelIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Cut is just copy followed by removeRows -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QItemSelectionRange&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selectionModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;removeRows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That puts in place the missing piece in the chain from the UI trigger to the model methods being called, completing our cut-copy-paste implementation.&lt;/p&gt;

&lt;p&gt;Err, maybe not.&lt;/p&gt;

&lt;h1 id=&quot;enablingdisabling-the-actions&quot;&gt;Enabling/Disabling the actions&lt;/h1&gt;
&lt;p&gt;There is still the little matter of enabling/disabling the menu actions based on certain conditions.&lt;/p&gt;

&lt;p&gt;Each action should be enabled only when -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Cut&lt;/strong&gt;: focus widget has a selection, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cut&lt;/code&gt; slot and model allows delete&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Copy&lt;/strong&gt;: focus widget has a selection and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy&lt;/code&gt; slot&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Paste&lt;/strong&gt;: focus widget has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;paste&lt;/code&gt; slot and can accept the clipboard item&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since these actions belong to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClipboardHelper&lt;/code&gt;, we extend that class to track when the focus widget changes by connecting it to a slot -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SIGNAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SLOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updateCutCopyStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the slot, we can check if the focus widget has a current selection and cut/copy slot and enable/disable the actions accordingly. We also need to take care of the case where the selection state changes without the focus widget being changed. We do that by tracking the selection change of the focus widget in another slot -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updateCutCopyStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;dynamic_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;disconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectionModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                   &lt;span class=&quot;n&quot;&gt;SIGNAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectionChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                           &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;n&quot;&gt;SLOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidgetSelectionChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                                    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// No focus widget to copy from&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMetaObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;meta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metaObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexOfSlot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;copy()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Focus Widget doesn&apos;t have a copy slot&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;dynamic_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectionModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SIGNAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectionChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;SLOT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidgetSelectionChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                                 &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasSelection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// view doesn&apos;t have anything selected to copy&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canCut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// focus widget has a selection and copy slot: copy possible&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidgetSelectionChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QItemSelection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*deselected*/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Selection changed in the XTableView that has focus&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;dynamic_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qApp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionCopy_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionCut_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                                &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canCut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Updating the paste action status is much more straight forward -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClipboardHelper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updatePasteStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;QWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qApp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// No focus widget to paste into&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QGuiApplication&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clipboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;formats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Nothing on clipboard to paste&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMetaObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;meta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metaObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexOfSlot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;paste()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Focus Widget doesn&apos;t have a paste slot&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;dynamic_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;focusWidget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canPaste&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Focus widget view cannot accept this item&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Focus widget can accept this item: paste possible&quot;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actionPaste_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While implementing the above, we added an expectation that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XTableView&lt;/code&gt; will provide a few more methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canCut()&lt;/code&gt; etc. which we now add and implement -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasSelection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectionModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectedIndexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canCut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This is a heuristic&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supportedDropActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Qt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canPaste&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QMimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canDropMimeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Qt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CopyAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QModelIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XTableView::canCut()&lt;/code&gt; should ideally check if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;removeRows()&lt;/code&gt; is implemented by the model. I couldn’t find a clean portable way to check that. So, instead I implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;supportedDropActions()&lt;/code&gt; to return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Qt::IgnoreAction&lt;/code&gt; in all the models that are read-only and don’t implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;removeRows()&lt;/code&gt;; the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QAbstractItemModel::supportedDropActions()&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Qt::CopyAction&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The advantage of creating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XTableView&lt;/code&gt; with cut/copy/paste slots is that several native Qt widgets like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QLineEdit&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTextEdit&lt;/code&gt; etc. also have similar named slots. So our code should work with those widgets too. That was the plan. Practically, they don’t have hasSelection/canCut/canPaste methods so they won’t work because the actions won’t be enabled.&lt;/p&gt;

&lt;p&gt;All is not lost, though! What the native widgets do support is the cut/copy/paste actions if the short cut key combinations (Ctrl/Cmd-x/c/v) are received by the widget.&lt;/p&gt;

&lt;h1 id=&quot;shortcut-keys&quot;&gt;Shortcut keys&lt;/h1&gt;
&lt;p&gt;If you look at the code where we created the actions in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClipboardHelper&lt;/code&gt; constructor, you will notice that I did not associate any keyboard shortcut with the actions. That’s deliberate.&lt;/p&gt;

&lt;p&gt;Normally, when you press any key or key combination, the current focus widget receives a key press event. Most native Qt widgets like line edits and text edits etc. will do a cut, copy, paste in response to that event without any additional code required. However, if you associate keyboard shortcuts for menu actions, the keypress events are captured by the menu and are not sent to the widget.&lt;/p&gt;

&lt;p&gt;In other words by not associating shortcut keys to the menu actions, we allow them to be received by the focus widget which in turn does the right thing. So, even if the menu items for cut/copy/paste may be disabled, the functionality will still work if shortcut keys are used!&lt;/p&gt;

&lt;p&gt;However, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QTableView&lt;/code&gt; doesn’t process those key combinations. Which means the shortcut keys don’t work with model-view windows. We fix that by implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XTableView::keyPressEvent()&lt;/code&gt; -&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyPressEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QKeyEvent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QKeySequence&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QKeySequence&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QKeySequence&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Paste&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;paste&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;QTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyPressEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I hope you found this useful! Let me know in the comments below.&lt;/p&gt;
</description>
          <pubDate>Tue, 07 Apr 2020 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/qt-cut-copy-paste/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/qt-cut-copy-paste/</guid>
        </item>
    
  
    
	<item>
          <title>Cut Copy Paste with Ostinato - video</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;In the last few weeks, I added cut, copy, paste support in &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt;. It’s available for streams, device groups, statistics and logs.&lt;/p&gt;

&lt;p&gt;Menu actions are available in the Application Edit Menu and in the context menu for some of the windows. Irrespective of whether the menu item is available or not, you can always hit shortcut keys for your platform (e.g. ctrl-c, ctrl-v for Windows and cmd-c, cmd-v for MacOs).&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/AZ6QcKfE2e0?rel=0&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The feature will be part of the upcoming Ostinato 1.1.&lt;/p&gt;

&lt;p&gt;What’s your number one pain point with Ostinato that you’d like me to address? Let me know in the comments below.&lt;/p&gt;
</description>
          <pubDate>Wed, 25 Mar 2020 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-copy-paste/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-copy-paste/</guid>
        </item>
    
  
    
	<item>
          <title>Be More</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Based off a conversation with a friend -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/be-more.png&quot; alt=&quot;Be More&quot; /&gt;&lt;/p&gt;
</description>
          <pubDate>Tue, 14 Jan 2020 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/be-more/</link>
          <guid isPermaLink="true">https://srivatsp.com/be-more/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato Device Emulation - Video tutorial</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;em&gt;There have been indications (and some direct feedback) that there isn’t enough documentation about Ostinato and it’s various features. While there is the &lt;a href=&quot;https://userguide.ostinato.org/&quot;&gt;Ostinato User Guide&lt;/a&gt;, I believe people are looking for a tutorial or HOW TO kind of documentation. So here goes …&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a &lt;a href=&quot;https://groups.google.com/d/topic/ostinato/b8d7FVOQU4E/discussion&quot;&gt;question about Ostinato device groups&lt;/a&gt; was posted recently on the forum, I decided that it was time to make a tutorial about the feature. And as I tend to do, I went all ambitious and perfectionist about it and decided to do a video tutorial. I knew video would be a lot of work and time consuming, but it turned out to be even more than I thought. Although I’m still inclined to make tweaks to the video, it is time to curb that instinct and hit publish!&lt;/p&gt;

&lt;p&gt;So, here’s the result of the last 10 days of work on this tutorial. I hope it’s helpful.&lt;/p&gt;

&lt;h1 id=&quot;video-tutorial&quot;&gt;Video Tutorial&lt;/h1&gt;
&lt;p&gt;(&lt;em&gt;Watch full-screen for a better view&lt;/em&gt;)&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/6jv3fFyn82g?rel=0&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;text-recap&quot;&gt;Text Recap&lt;/h2&gt;
&lt;p&gt;Although the video goes into much more details (and &lt;em&gt;&lt;strong&gt;I highly recommend you watch it&lt;/strong&gt;&lt;/em&gt;), here’s a quick recap -&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/assets/images/ost-emul-dev-topo.png&quot; class=&quot;align-left&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/thumb-ost-emul-dev-topo.png&quot; alt=&quot;Test Topology&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the test topology on the left, we want to test if the Network under test (NUT) is routing traffic correctly between two subnets.&lt;/p&gt;

&lt;p&gt;The two edge routers &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PE1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PE2&lt;/code&gt; are connected to two ports on the Ostinato traffic generator.&lt;/p&gt;

&lt;p&gt;On Ostinato, select a port connected to one of the edge routers and follow these steps -&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a device group specifying
    &lt;ul&gt;
      &lt;li&gt;Device count&lt;/li&gt;
      &lt;li&gt;IP stack - IPv4/IPv6/Dual/None&lt;/li&gt;
      &lt;li&gt;IP address (range)&lt;/li&gt;
      &lt;li&gt;Gateway IP&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Create a stream
    &lt;ul&gt;
      &lt;li&gt;Ensure Src/Dst mac are set to “Resolve”&lt;/li&gt;
      &lt;li&gt;Populate Src/Dst IP - src IP must match one of the device IP&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Repeat the above steps on the second Ostinato port.&lt;/p&gt;

&lt;p&gt;Ostinato will automatically do a ARP and fill in the source and destination mac address appropriately.&lt;/p&gt;

&lt;p&gt;You can also look at the &lt;a href=&quot;https://userguide.ostinato.org/device-emulation/&quot;&gt;Ostinato user-guide page for Device Emulation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, hit the comments below. Also, let me know if you’d like to see more Ostinato tutorials and on which topics.&lt;/p&gt;

</description>
          <pubDate>Thu, 17 Oct 2019 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-device-emulation/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-device-emulation/</guid>
        </item>
    
  
    
	<item>
          <title>Brown Boy (Blaft)</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;My friend &lt;a href=&quot;https://twitter.com/ps_nissim&quot;&gt;P S Nissim&lt;/a&gt; self-published a Kindle short - &lt;em&gt;Brown Boy&lt;/em&gt; back in 2017.&lt;/p&gt;

&lt;p&gt;Earlier this year it was picked up and republished by &lt;a href=&quot;https://www.blaft.com&quot;&gt;Blaft&lt;/a&gt; - an indie publishing house based in Chennai.&lt;/p&gt;

&lt;p&gt;Working with Nissim and the publisher, I redesigned the cover art (&lt;em&gt;click the image to enlarge&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/assets/images/brown-boy-blaft.png&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/thumb-brown-boy-blaft.png&quot; alt=&quot;Brown Boy Blaft&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cover Design: Srivats P&lt;/p&gt;

&lt;p&gt;Here’s the &lt;a href=&quot;https://srivatsp.com/brown-boy/&quot;&gt;old Brown Boy cover&lt;/a&gt; for comparison.&lt;/p&gt;
</description>
          <pubDate>Tue, 17 Sep 2019 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/brown-boy-blaft/</link>
          <guid isPermaLink="true">https://srivatsp.com/brown-boy-blaft/</guid>
        </item>
    
  
    
	<item>
          <title>Crafting VxLAN packets using Ostinato</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;em&gt;This post was originally published in Sep 2011 at the Inficone, but that site is now off the Internet; so reposting it here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The buzzwords these days are “virtualization” and “cloud”. At the recently concluded VMworld 2011, VMware and Cisco &lt;a href=&quot;https://news.vmware.com/releases/vmw-cisco-vmworld-083011&quot;&gt;announced the VxLAN technology&lt;/a&gt; which was developed to address scalability problems in today’s data centers.&lt;/p&gt;

&lt;p&gt;Virtualized data centers today using traditional VLANs have scaling problems due to the 12-bit VLAN space which translates to a maximum of 4094 usable VLANs. Another requirement for multi-tenant networks is to isolate tenant traffic from one another. A VLAN could be used to provide this isolation on a L2 network but is again limited by the the 12-bit space. A L3 solution to this problem would force tenants to always use IP which would exclude tenants using non IP or other L2 protocols for inter-VM communication. Another problem is the mac address explosion at the Top of Rack switch which now needs to learn mac addresses per VM rather than per physical server.&lt;/p&gt;

&lt;p&gt;VxLAN (short for Virtual eXtensible Local Area Network) attempts to solve these problems by creating virtual overlay L2 networks for inter-VM communication on top of the physical L2/L3 networks. It does this by doing a MAC-in-IP tunnelling and introducing a 24-bit Virtual Network Identifier (VNI) space.&lt;/p&gt;

&lt;p&gt;For the full technical details on what problems VxLAN is attempting to solve and how, see the &lt;a href=&quot;http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-00&quot;&gt;IETF draft&lt;/a&gt; (&lt;em&gt;Update: Now RFC 7348&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Let’s look at the proposed frame format -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Outer Ethernet Header (including an optional VLAN tag)&lt;/li&gt;
  &lt;li&gt;Outer IP Header&lt;/li&gt;
  &lt;li&gt;Outer UDP Header&lt;/li&gt;
  &lt;li&gt;VxLAN Header&lt;/li&gt;
  &lt;li&gt;Inner Ethernet Header (including an optional VLAN tag)&lt;/li&gt;
  &lt;li&gt;Original Ethernet Payload (excluding the original Ethernet FCS)&lt;/li&gt;
  &lt;li&gt;FCS for Outer Ethernet Frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ostinato supports all the above protocols except the new VxLAN Header. We can simulate the VxLAN header very easily using Ostinato’s &lt;a href=&quot;https://userguide.ostinato.org/user-script/&quot;&gt;Userscript&lt;/a&gt; protocol.&lt;/p&gt;

&lt;p&gt;Before we write the VxLAN userscript let’s look at the VxLAN frame format&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|R|R|R|R|I|R|R|R|            Reserved                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                VXLAN Network Identifier (VNI) |   Reserved    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s a simple 8 byte header with 2 fields - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flags&lt;/code&gt; (8-bit) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VNI&lt;/code&gt; (24-bit) while the remaining fields are all reserved and MUST to be set to 0. The flag field has only one defined bit - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I&lt;/code&gt; which indicates that the header contains a valid VNI tag while the remaining bits MUST be set to 0.&lt;/p&gt;

&lt;p&gt;Based on this information, here’s the VxLAN userscript -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;protocol.name = &quot;VxLAN&quot;
protocol.protocolFrameSize = function() {
  return 8;
}
protocol.protocolFrameValue = function(index) {
    var flags  = 0x08; // set I bit, reset all R bits
    var vni  = 0x749127; // example VNI value

    var pfv = new Array(8);

    pfv[0] = flags;
    pfv[1] = 0;
    pfv[2] = 0;
    pfv[3] = 0;

    pfv[4] = (vni &amp;gt;&amp;gt; 16) &amp;amp; 0xFF;
    pfv[5] = (vni &amp;gt;&amp;gt; 8) &amp;amp; 0xFF;
    pfv[6] = vni &amp;amp; 0xFF;
    pfv[7] = 0;

    return pfv;
}
protocol.protocolId = function() {
    return 4789; // IANA assigned
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We first define the protocol’s name and its size. We then define the contents of the protocol as an array which we populate with the flag field set to 0x08 (I bit set, R bits reset) and an example VNI value of 0x749127. Since VxLAN is a payload for UDP, we define the port number that UDP should use in the protocolId() function. That’s all that is required to define the VxLAN protocol.&lt;/p&gt;

&lt;p&gt;To configure the full tunnelled frame format in Ostinato, create a new stream, edit it - go to “Advanced Protocol Selection” and add the following protocols in the given order -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;MAC&lt;/li&gt;
  &lt;li&gt;Eth II&lt;/li&gt;
  &lt;li&gt;IPv4&lt;/li&gt;
  &lt;li&gt;UDP&lt;/li&gt;
  &lt;li&gt;UserScript&lt;/li&gt;
  &lt;li&gt;MAC&lt;/li&gt;
  &lt;li&gt;Eth II&lt;/li&gt;
  &lt;li&gt;IPv4&lt;/li&gt;
  &lt;li&gt;DATA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/assets/images/ost-vxlan-proto.png&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-vxlan-proto.png&quot; alt=&quot;VxLAN frame protocols&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to protocol data tab, select UDP and override the UDP checksum field to set it as 0 (recommended by the VxLAN draft).&lt;/p&gt;

&lt;p&gt;Next goto Userscript, copy paste the above script and click on “Compile” - you should see the compilation result as “Success”. Configure any other protocol fields that you may want and then click OK.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/assets/images/ost-vxlan-proto-data.png&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-vxlan-proto-data.png&quot; alt=&quot;VxLAN user script&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on Apply and hit play/transmit to send your VxLAN Tunnelled frames.&lt;/p&gt;

&lt;p&gt;The following snapshot shows how the final packet would look like including the just created VxLan.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/assets/images/ost-vxlan-frame.png&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-vxlan-frame.png&quot; alt=&quot;VxLAN user script&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make things even more simpler, you can &lt;a href=&quot;https://srivatsp.com/assets/others/vxlan.osts&quot;&gt;open this stream file containing a VxLAN stream&lt;/a&gt; in Ostinato.&lt;/p&gt;

&lt;p&gt;You might find these useful as well -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://srivatsp.com/wireshark/vxlan-wireshark-dissector/&quot;&gt;How to write a Wireshark Lua dissector for VxLAN&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-geneve/&quot;&gt;Generating Geneve traffic&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-mpls-userscript/&quot;&gt;How to write a Ostinato MPLS user script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Thu, 29 Aug 2019 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/craft-vxlan-packets-using-ostinato/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/craft-vxlan-packets-using-ostinato/</guid>
        </item>
    
  
    
	<item>
          <title>Analyzing VxLAN packets using Wireshark</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;em&gt;This post was originally published in Nov 2011 at &lt;strong&gt;Love My Tool&lt;/strong&gt; (rebranded as &lt;a href=&quot;https://www.networkdatapedia.com&quot;&gt;Network Data Pedia&lt;/a&gt; recently), but is no longer available there; so reposting it here. Since the original post, Wireshark has introduced a built-in VxLAN dissector - but this post is still instructive for how to create a Lua dissector&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As virtualization and cloud computing platforms are getting more and more deployed, the L2 network that the VMs are deployed in is exploding in size and VM migration from one host to another becoming problematic. To solve this problem VMware and Cisco have introduced a new protocol - &lt;a href=&quot;https://blogs.vmware.com/console/2011/08/towards-virtualized-networking-for-the-cloud.html&quot;&gt;VxLAN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To stay on topic, we will not concern ourselves about the protocol details but only about the protocol frame format. For more information on how the protocol works and what it does see &lt;a href=&quot;https://codingrelic.geekhold.com/2011/09/care-and-feeding-of-vxlan.html&quot;&gt;Denton Gentry’s 3-part VxLAN article&lt;/a&gt; or go straight to the &lt;a href=&quot;https://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-00&quot;&gt;VxLAN IETF Draft&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being a new protocol, Wireshark doesn’t yet have a built-in dissector for the VxLAN protocol. Anticipating such scenarios, Wireshark exports a Lua interface for creating quick-and-dirty dissectors. We will use this interface to write a Wireshark Lua dissector for VxLAN.&lt;/p&gt;

&lt;p&gt;The VxLAN frame is a Ethernet-in-UDP encapsulated frame i.e. Ethernet-IP-UDP-VxLAN-Ethernet-IP. If you open &lt;a href=&quot;https://srivatsp.com/assets/others/vxlan.pcap&quot;&gt;vxlan.pcap&lt;/a&gt; in Wireshark, you will see that Wireshark stops dissecting as soon as it encounters the VxLAN header and shows all remaining bytes after UDP as just data bytes. The reason why Wireshark stops there is it does not understand how to decode the VxLAN header. That is what we will fix with our Lua script&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/assets/images/wireshark_without_vxlan.png&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/wireshark_without_vxlan.png&quot; alt=&quot;Wireshark without VxLAN&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we look at the Lua code itself, let’s look at the VxLAN header format -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|R|R|R|R|I|R|R|R|            Reserved                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                VXLAN Network Identifier (VNI) |   Reserved    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It’s a simple 8 byte header - a 8-bit flags field starting at offset 0 with only one bit currently defined while the remaining 7 are reserved, and a 24-bit VNI Tag starting at offset 4. All the remaining bytes in the header are reserved.&lt;/p&gt;

&lt;p&gt;Without further ado, here’s the VxLAN Lua dissector code -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;do
    local p_vxlan = Proto(&quot;vxlan&quot;,&quot;Virtual eXtended LAN&quot;);

    local f_flags = ProtoField.uint8(&quot;vxlan.flags&quot;, &quot;Flags&quot;, base.HEX)
    local f_flag_i = ProtoField.bool(&quot;vxlan.flags.i&quot;, &quot;I Flag&quot;, 8,
             {&quot;Valid VNI Tag present&quot;, &quot;Valid VNI Tag NOT present&quot;}, 0x08)
    local f_rsvd1 = ProtoField.uint24(&quot;vxlan.rsvd1&quot;, &quot;Reserved&quot;, base.HEX)
    local f_vni = ProtoField.uint24(&quot;vxlan.vni&quot;, &quot;VNI&quot;, base.HEX)
    local f_rsvd2 = ProtoField.uint8(&quot;vxlan.rsvd2&quot;, &quot;Reserved&quot;, base.HEX)

    p_vxlan.fields = {f_flags, f_flag_i, f_rsvd1, f_vni, f_rsvd2}

    function p_vxlan.dissector(buf, pinfo, root)

        local t = root:add(p_vxlan, buf(0,8))

        local f = t:add(f_flags, buf(0,1))
        f:add(f_flag_i, buf(0,1))

        t:add(f_rsvd1, buf(1,3))
        t:add(f_vni, buf(4,3))
        t:add(f_rsvd2, buf(7,1))

        t:append_text(&quot;, VNI: 0x&quot; .. string.format(&quot;%x&quot;, buf(4, 3):uint()))

        local eth_dis = Dissector.get(&quot;eth&quot;)
        eth_dis:call(buf(8):tvb(), pinfo, root)
    end

    local udp_encap_table = DissectorTable.get(&quot;udp.port&quot;)
    udp_encap_table:add(9029, p_vxlan)
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Let’s examine the code closely.&lt;/p&gt;

&lt;p&gt;We start by putting the entire dissector in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do-end&lt;/code&gt; code block to restrict the scope of the local variables we use.&lt;/p&gt;

&lt;p&gt;We call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proto()&lt;/code&gt; to create our protocol &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p_vxlan&lt;/code&gt;. Then we create one &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProtoField&lt;/code&gt; for each field in the header. Notice that we use the appropriate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProtoField.&amp;lt;type&amp;gt;&lt;/code&gt; for each field based on the VxLAN header format above. Once all fields are defined, we set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p_vxlan.fields&lt;/code&gt; to a table containing all the fields.&lt;/p&gt;

&lt;p&gt;Next we define our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p_vxlan&lt;/code&gt; dissector function. This function takes 3 input parameters - the packet buffer that we need to dissect, packet information containing meta data like length of the frame, captured time etc. and the root of the dissected tree to which we will add the dissected information.&lt;/p&gt;

&lt;p&gt;The first thing that we add to the root of the tree is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p_vxlan&lt;/code&gt; protocol specifying that it starts at offset 0 and spans 8 bytes.&lt;/p&gt;

&lt;p&gt;Next we add each field, in the order in which they are defined in the header, as subitems of our protocol. Note that we add the &lt;em&gt;I Flag&lt;/em&gt; field as a subitem of the &lt;em&gt;Flag&lt;/em&gt; field instead of adding it as a subitem of our protocol.&lt;/p&gt;

&lt;p&gt;After adding all the fields, we append our Protocol Name with the most important field of the protocol - VNI so that the value is visible even without expanding the protocol tree.&lt;/p&gt;

&lt;p&gt;Before we end the function, recall that VxLAN is an encapsulated packet format and there’s another packet starting with the Ethernet MAC addresses following the VxLAN header. So we call the ethernet dissector and pass it the buffer from offset 8 (after the VxLAN header).&lt;/p&gt;

&lt;p&gt;The final step in our script is to register the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p_vxlan&lt;/code&gt; protocol as a UDP encapsulated dissector. In this script, we are using 9029 as an example UDP port number since IANA hasn’t assigned an official port number yet.&lt;/p&gt;

&lt;p&gt;That’s all there is to writing our VxLAN Lua dissector.&lt;/p&gt;

&lt;p&gt;Now we need to make Wireshark use this dissector. Open Wireshark, go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Help|About|Folders&lt;/code&gt; and look for &lt;em&gt;Personal Plugins&lt;/em&gt; and &lt;em&gt;Global Plugins&lt;/em&gt; - create a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vxlan.lua&lt;/code&gt; in either of those two locations and then restart Wireshark. Go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Help|About|Plugins&lt;/code&gt; and verify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vxlan.lua&lt;/code&gt; is listed.&lt;/p&gt;

&lt;p&gt;Now open &lt;a href=&quot;https://srivatsp.com/assets/others/vxlan.pcap&quot;&gt;vxlan.pcap&lt;/a&gt; in Wireshark to see vxlan.lua in action!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/assets/images/wireshark_with_vxlan.png&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/wireshark_with_vxlan.png&quot; alt=&quot;Wireshark with VxLAN&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a Lua primer and language reference see &lt;a href=&quot;https://www.lua.org/pil/&quot;&gt;Programming in Lua&lt;/a&gt; . For Wireshark specific Lua information, see &lt;a href=&quot;https://www.wireshark.org/docs/wsdg_html_chunked/wsluarm.html&quot;&gt;Lua support in Wireshark&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To create your own VxLAN packets, see &lt;a href=&quot;https://srivatsp.com/ostinato/craft-vxlan-packets-using-ostinato/&quot;&gt;Crafting VxLAN packets using Ostinato&lt;/a&gt;.&lt;/p&gt;
</description>
          <pubDate>Tue, 13 Aug 2019 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/wireshark/vxlan-wireshark-dissector/</link>
          <guid isPermaLink="true">https://srivatsp.com/wireshark/vxlan-wireshark-dissector/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato throughput on a 40G NIC</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p class=&quot;notice--info&quot;&gt;&lt;strong&gt;Update (2021)&lt;/strong&gt;: Ostinato now supports 10G, 25G and 40Gbps line-rate rate traffic using the &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;Turbo Transmit&lt;/a&gt; add-on.&lt;/p&gt;

&lt;p&gt;A few weeks ago I attended the &lt;a href=&quot;https://dpdksummitbangalore2019.sched.com&quot;&gt;DPDK Summit&lt;/a&gt; in Bangalore, where &lt;a href=&quot;https://www.linkedin.com/in/muthurajan-jayakumar-9237191/&quot;&gt;M Jay&lt;/a&gt; from Intel loaned me a dual-port 40G interface to enable Ostinato to become a high-speed packet generator using DPDK. &lt;em&gt;Thanks M Jay and Intel!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you’ve been following Ostinato for a long time, you might recall I had done a prototype of Ostinato with DPDK back in 2014 which won the &lt;a href=&quot;https://www.dpdk.org/event/dpdk-contest-2014/&quot;&gt;6Wind DPDK Design contest&lt;/a&gt;. That work was done on a VM using VNICs instead of physical NICs and the lack of the latter (amongst other things) meant I never productized that code.&lt;/p&gt;

&lt;p&gt;Before I (re)start the DPDK work on Ostinato, I wanted to get some baseline measurements with the existing libpcap based code. This blog post presents these results.&lt;/p&gt;

&lt;p&gt;You can also look at the results for a 1G port for &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-ubuntu-maximum-transmit-rate/&quot;&gt;Linux&lt;/a&gt;, &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-macos-maximum-transmit-rate/&quot;&gt;MacOS&lt;/a&gt;, &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-live-iso-maximum-transmit-rate/&quot;&gt;Live ISO&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I configured the same single stream as in previous tests -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Protocols: Mac, Ethernet II, IPv4, UDP, Pattern&lt;/li&gt;
  &lt;li&gt;Source and Destination Mac addresses populated with the actual Mac addresses of the source and sink&lt;/li&gt;
  &lt;li&gt;Source and Destination IP addresses populated with the actual IP addresses of the source and sink&lt;/li&gt;
  &lt;li&gt;Packets/sec: 0&lt;/li&gt;
  &lt;li&gt;Number of packets: 10 (default)&lt;/li&gt;
  &lt;li&gt;After this stream: Goto first&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One difference from the previous tests is that I’m using a single host with a dual-port NIC with the ports connected back to back. Ostinato packets sent on port 1 are received back on port 2 on the &lt;strong&gt;same host&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Also, I’m adding two jumbo sized packet sizes to this test - 4096 bytes and 9018 bytes.&lt;/p&gt;

&lt;h1 id=&quot;performance-figures&quot;&gt;Performance figures&lt;/h1&gt;
&lt;p&gt;Here are the numbers -&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Packet Size&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Send Rate (Kpps)&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Recv Rate (Kpps)&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Send Rate (Mbps)&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Recv Rate (Mbps)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;64&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;755&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;630&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;507&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;423&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;128&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;753&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;640&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;892&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;758&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;256&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;738&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;635&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;1,630&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,402&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;512&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;732&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;636&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;3,115&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;2,707&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;1024&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;725&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;642&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;6,055&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5,362&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;1518&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;725&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;639&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;8,920&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7,862&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;4096&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;574&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;566&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;18,901&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;18,637&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;9018&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;225&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;225&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;16,268&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;16,268&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Mbps rate was calculated using the below formula to take into account the ethernet line overhead of 20 bytes (1 byte SFD + 7 byte preamble + 12 byte inter-packet gap) -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Rate (in Mbps) = (PacketSize + 20) * KppsRate * 8 / 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;specs&quot;&gt;Specs&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Software&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://ostinato.gumroad.com/l/ostlnx09&quot;&gt;Ostinato 0.9 - for Linux (Ubuntu 16.04, 64-bit)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Ubuntu 16.04.3 LTS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Processor: Xeon E5-2620v4 @ 2.10GHz (8-core)&lt;/li&gt;
  &lt;li&gt;RAM: 32GB&lt;/li&gt;
  &lt;li&gt;NIC: Intel XL710 40Gbps dual-port PCI 3.0, x8&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;observations&quot;&gt;Observations&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;The first that jumped out was that there was a lot of fluctuation in the send rate while transmitting e.g. for 256 bytes packets, I saw rates ranging from 635Kpps to 728Kpps. This is a very wide range and I don’t have an explanation. I have used the lower end of the range seen in the above results for all packet sizes.&lt;/li&gt;
  &lt;li&gt;The second thing that jumps out from the results table is that receive rate gets capped at ~640Kpps even if the send rate is higher. Again, I don’t have an explanation for the same.&lt;/li&gt;
  &lt;li&gt;The next thing that is suprising is that the max rate is achieved for 4096 packets, instead of 9018.&lt;/li&gt;
  &lt;li&gt;Although it’s a 8-core CPU, only two core reach 100% CPU during the test - this is expected since for a single port, Ostinato doesn’t use more than one core and we have both transmit and receive port on the same host. The first core with 100% cpu utilization (presumably the one handling the tx port) has 15% user and 85% system usage - indicating that the major time is spent in the kernel driver. The second core had 100% utilization in softirq - presumably the one handling the receive port&lt;/li&gt;
  &lt;li&gt;RAM usage not measured&lt;/li&gt;
  &lt;li&gt;One heartening observation is that Ostinato (even without DPDK) can be used as a 10G traffic generator - if you can use jumbo frames.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more Ostinato posts, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Tue, 02 Apr 2019 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-40g-maximum-transmit-rate/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-40g-maximum-transmit-rate/</guid>
        </item>
    
  
    
	<item>
          <title>How to change the default boot option in Ostinato virtual appliance</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;The Ostinato virtual appliance has two boot options -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Boot Ostinato (Agent + Controller)&lt;/li&gt;
  &lt;li&gt;Boot Drone (Agent only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-virt-boot.png&quot; alt=&quot;Ostinato Virtual Appliance Boot Screen&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Boot Ostinato&lt;/em&gt; is the default and therefore is selected and if you don’t change it within 30s, the boot proceeds with the same.&lt;/p&gt;

&lt;p&gt;For some use cases you might want the default option to be &lt;em&gt;Boot Drone&lt;/em&gt; e.g. for automation use cases using the &lt;a href=&quot;https://apiguide.ostinato.org/tutorial/&quot;&gt;Python API&lt;/a&gt; where your automation framework boots appliances or if you will always use the &lt;a href=&quot;https://userguide.ostinato.org/architecture/&quot;&gt;GUI controller remotely&lt;/a&gt; or if you just want the appliance to boot faster (Agent only boots a shade faster than Agent + Controller)&lt;/p&gt;

&lt;p&gt;The Ostinato virtual appliance uses the &lt;a href=&quot;https://wiki.syslinux.org/wiki/index.php?title=EXTLINUX&quot;&gt;EXTLINUX&lt;/a&gt; bootloader from the &lt;a href=&quot;https://wiki.syslinux.org/wiki/index.php?title=The_Syslinux_Project&quot;&gt;SYSLINUX project&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To change the default option to &lt;em&gt;Boot Drone&lt;/em&gt; instead, you need to change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DEFAULT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ONTIMEOUT&lt;/code&gt; statements in the extlinux config file.&lt;/p&gt;

&lt;p&gt;Here are the detailed steps -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Boot the Ostinato Virtual Appliance&lt;/li&gt;
  &lt;li&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Boot Drone (Agent only)&lt;/code&gt; from the boot menu&lt;/li&gt;
  &lt;li&gt;After the appliance boots and you get a shell prompt (&lt;em&gt;press Enter a couple of times if you don’t see the prompt&lt;/em&gt;), run the following commands -
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd /mnt/sda1/boot/extlinux
sudo sed -i &quot;s/DEFAULT ostinato/DEFAULT drone/&quot; extlinux.conf
sudo sed -i &quot;s/ONTIMEOUT ostinato/ONTIMEOUT drone/&quot; extlinux.conf
sudo reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the reboot (and for all subsequent boots), &lt;em&gt;Boot Drone (Agent only)&lt;/em&gt; will be set as the default boot option.&lt;/p&gt;

&lt;p&gt;Note that after booting into agent only mode, if you decide you want to the start the Ostinato GUI controller, you don’t need to reboot, just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to change other aspects of the boot menu (e.g. the timeout value), lookup the &lt;a href=&quot;https://wiki.syslinux.org/wiki/index.php?title=Menu&quot;&gt;EXTLINUX boot menu documentation&lt;/a&gt; - Ostinato uses the &lt;em&gt;Simple Menu System&lt;/em&gt; - and change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extlinux.conf&lt;/code&gt; accordingly.&lt;/p&gt;

&lt;p&gt;For more Ostinato posts, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Sun, 24 Mar 2019 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-virtual-change-default-boot/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-virtual-change-default-boot/</guid>
        </item>
    
  
    
	<item>
          <title>Hell&apos;s Kitchen</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;No, this is not about the cookery reality TV show.&lt;/p&gt;

&lt;p&gt;Back in 1996, for our graduate project I and &lt;a href=&quot;https://www.linkedin.com/in/mudit-varma-2a18a360/&quot;&gt;Mudit&lt;/a&gt; wrote an arcade-style side-scroller game that we called &lt;em&gt;Hell’s Kitchen&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I recently discovered the files in a backup archive and had the urge to play it once again. Although, I had the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.exe&lt;/code&gt; file - this game was built to run on MS DOS. And as we know those old DOS games don’t work on recent Windows anymore.&lt;/p&gt;

&lt;p&gt;So, as any self-respecting nerd would do, I created a &lt;a href=&quot;http://www.freedos.org&quot;&gt;FreeDOS&lt;/a&gt; VM using &lt;a href=&quot;https://www.virtualbox.org&quot;&gt;VirtualBox&lt;/a&gt; and transferred the games from my Windows 10 host to the DOS VM using a temporary ISO.&lt;/p&gt;

&lt;p&gt;I still wasn’t sure if it would work ‘coz the game used &lt;a href=&quot;https://en.wikipedia.org/wiki/Mode_X&quot;&gt;Mode X&lt;/a&gt; and a bunch of other low level hardware and VGA reprogramming stuff like &lt;a href=&quot;https://en.wikipedia.org/wiki/Color_cycling&quot;&gt;color cycling&lt;/a&gt;, video memory page flipping, a new keyboard ISR etc.&lt;/p&gt;

&lt;p&gt;It worked!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/HellsKitchen.gif&quot; alt=&quot;Hell&apos;s Kitchen animated GIF&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sure, some things are not right - the color cycling is not right, the sound effects don’t work, the end credits are too fast, but the essential game play all works pretty well!&lt;/p&gt;

&lt;p&gt;And some of the stuff is cringeworthy - especially the intro and end credits - but I view that as a sign of aesthetic growth.&lt;/p&gt;

&lt;p&gt;A shout out to &lt;a href=&quot;https://twitter.com/connectmc&quot;&gt;Sudarshan Purohit&lt;/a&gt; who did the game design and Justin Fernandes (RIP) who drew the sprites for the main character - Masala Das and the other assorted powerups and lives.&lt;/p&gt;

&lt;p&gt;Another shout out to &lt;a href=&quot;https://en.wikipedia.org/wiki/Michael_Abrash&quot;&gt;Michael Abrash&lt;/a&gt; who introduced and inspired the whole endeavour via his book &lt;a href=&quot;https://www.amazon.com/Power-Graphics-Programming-Michael-Abrash/dp/0880225009&quot;&gt;Power Graphics Programming&lt;/a&gt; that I picked up on a whim at the Darya Ganj Sunday flea market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (Jan 2023)&lt;/strong&gt;: Now you can play Hell’s Kitchen directly in the browser thanks to the magic of &lt;a href=&quot;https://webassembly.org/&quot;&gt;WebAssembly&lt;/a&gt;, &lt;a href=&quot;https://www.freedos.org/&quot;&gt;FreeDOS&lt;/a&gt; and &lt;a href=&quot;https://js-dos.com/&quot;&gt;js-dos&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/projects/hells-kitchen/play/&quot; class=&quot;btn btn--success btn--x-large&quot;&gt;Play Hell’s Kitchen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are still reading this and want to play it locally, here’s a OVA file of the VM that you could download and import into your favourite hypervisor.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://srivatsp.com/assets/others/HellsKitchen.ova&quot;&gt;Download Hell’s Kitchen (5MB OVA file)&lt;/a&gt;&lt;/p&gt;

</description>
          <pubDate>Tue, 16 Oct 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/hells-kitchen/</link>
          <guid isPermaLink="true">https://srivatsp.com/hells-kitchen/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato API - Wait for stream transmission to finish</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;The Ostinato API &lt;a href=&quot;https://apiguide.ostinato.org/tutorial/&quot;&gt;tutorial&lt;/a&gt; configures a single stream to send 5 packets then sleeps for 7 seconds - assuming transmission would have finished by then - before fetching the tx/rx stats for verification.&lt;/p&gt;

&lt;p&gt;This is fine for a tutorial, but for production scenarios you will want to wait till the transmission is finished instead of waiting (sleeping) for a specified amount of time.&lt;/p&gt;

&lt;p&gt;Here’s how you can do that -&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# wait for transmit to finish
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tx_stats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;drone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx_port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;port_stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_transmit_on&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;transmit interrupted by user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Interested in more Ostinato tips and tricks? &lt;a href=&quot;#subscribe&quot;&gt;Subscribe&lt;/a&gt; to receive email updates!&lt;/p&gt;
</description>
          <pubDate>Tue, 26 Jun 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-api-stream-tx-finish/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-api-stream-tx-finish/</guid>
        </item>
    
  
    
	<item>
          <title>How to run Ostinato in a ssh-only environment</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Ostinato has a &lt;a href=&quot;https://userguide.ostinato.org/architecture/&quot;&gt;controller-agent architecture&lt;/a&gt; that uses a TCP connection (on port 7878 by default) for communication between them for command and control.&lt;/p&gt;

&lt;p&gt;One popular deployment of this architecture is where the agent runs on a headless server and connects to the DUT via direct ethernet links. Both the DUT and server are co-located in a lab. The GUI controller on the other hand runs on users’ laptop or desktop which is on the corporate network and different from the lab network. For the agent and controller to be able to talk to each other they need to be able to talk TCP over port 7878 (or another user specified port) between these two networks.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://userguide.ostinato.org/images/archBasic.png&quot; alt=&quot;Deployment&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Some environments may not allow any communication between these two networks except ssh (tcp port 22) which leads to a problem in running Ostinato.&lt;/p&gt;

&lt;p&gt;To enable Ostinato agent-controller communication in this restricted environment, one can use ssh tunnelling.&lt;/p&gt;

&lt;p&gt;Assuming you are running the agent (drone) on host &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bar&lt;/code&gt; and the Ostinato controller GUI on host &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;, run the following command on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;foo$ ssh -L 7878:localhost:7878 bar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above command will create a local server on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; listening on port 7878 and any TCP traffic to this port will be transparently tunnelled to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bar:7878&lt;/code&gt; over ssh. The tunnelled traffic will be encrypted as well which should be enough to bypass most firewalls.&lt;/p&gt;

&lt;p&gt;Now on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;, when you open the Ostinato controller GUI, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt; local portgroup will try to contact the local server running on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo:7878&lt;/code&gt; which in turn will contact the agent running on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bar:7878&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to learn more about ssh tunnelling, &lt;a href=&quot;https://chamibuddhika.wordpress.com/2012/03/21/ssh-tunnelling-explained/&quot;&gt;SSH Tunnelling explained&lt;/a&gt; is a good read.&lt;/p&gt;

&lt;p&gt;Interested in more Ostinato tips and tricks? &lt;a href=&quot;#subscribe&quot;&gt;Subscribe&lt;/a&gt; to receive email updates!&lt;/p&gt;
</description>
          <pubDate>Tue, 19 Jun 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-ssh-tunneling/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-ssh-tunneling/</guid>
        </item>
    
  
    
	<item>
          <title>Mid Air (cover)</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A cover of Paul Buchanan’s &lt;em&gt;Mid Air&lt;/em&gt;&lt;/p&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;166&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allow=&quot;autoplay&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/450766038&amp;amp;color=%23ff5500&amp;amp;auto_play=false&amp;amp;hide_related=false&amp;amp;show_comments=true&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Piano and vocals: Srivats P.&lt;/p&gt;

&lt;p&gt;Recorded on phone.&lt;/p&gt;
</description>
          <pubDate>Tue, 29 May 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/mid-air/</link>
          <guid isPermaLink="true">https://srivatsp.com/mid-air/</guid>
        </item>
    
  
    
	<item>
          <title>How to generate MPLS packets using Ostinato User Script</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; provides two ways to generate packets for protocols that are not available out of the box.&lt;/p&gt;

&lt;p&gt;In the last post, we saw &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-mpls-hexdump/&quot;&gt;how to create a MPLS packet in Ostinato using hexdump&lt;/a&gt;. In this post we will see how to do the same using a user script instead.&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;Although this post talks about MPLS, the same method is applicable for any protocol - MPLS is used just as an example here&lt;/p&gt;

&lt;p&gt;First we start with the MPLS packet format. Let’s assume we need to generate a IP/UDP packet encapsulated inside a MPLS tunnel. So the packet would consist of the following protocol headers in this order -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+----------+------+----+-----+------+
| Ethernet | MPLS | IP | UDP | Data |
+----------+------+----+-----+------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All the protocols in the above frame except MPLS are already supported in Ostinato.&lt;/p&gt;

&lt;p&gt;To create this packet in Ostinato, we create a stream and select the already available protocols - Ethernet II, IPv4, UDP, Pattern and then switch to the advanced protocol selection mode -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-us-mpls-simple-proto.png&quot; alt=&quot;Simple Protocols&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:{Script}[EXPERIMENTAL]&lt;/code&gt; protocol in the &lt;em&gt;Available Protocols&lt;/em&gt; list on the left, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPv4&lt;/code&gt; in the &lt;em&gt;Selected Protocols&lt;/em&gt; list on the right and click on the &lt;em&gt;right arrow&lt;/em&gt; to insert userscript in between Eth II and IPv4 -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-us-mpls-adv-proto.png&quot; alt=&quot;Advanced Protocols&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;You can use the &lt;em&gt;up/down arrows&lt;/em&gt; to change the order of the selected protocols at any time&lt;/p&gt;

&lt;p&gt;Now that we have the user script header at the correct place in the packet, let’s switch to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Protocol Data&lt;/code&gt; tab and click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserScript&lt;/code&gt; -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-us-mpls-proto-data.png&quot; alt=&quot;Protocol Data&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We need to write a script here to generate the MPLS header. To do that, let’s review the MPLS header format.&lt;/p&gt;

&lt;p&gt;The MPLS header consists of one or more MPLS tags comprising a &lt;em&gt;tag stack&lt;/em&gt; -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+-------+-------+-------+-----+
| Tag 1 | Tag 2 | Tag 3 | ... |
+-------+-------+-------+-----+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each &lt;em&gt;tag&lt;/em&gt; is 32-bit wide and is composed of the following fields -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+------------+-----+-----+-----+
| MPLS Label | Exp |  S  | TTL |
|    (20)    | (3) | (1) | (8) |
+------------+-----+-----+-----+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;The numbers in () represents the bit-width of each field. The S bit is set to 1 for the last (innermost) tag and is 0 for all the other tags&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s create a single tag MPLS header with the following values -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Label = 12701
EXP = 5
S = 1
TTL = 64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We start the script with defining the protocol name -&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MPLS&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;followed by the size of the header -&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolFrameSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;notice--warning&quot;&gt;Note: protocolFrameSize must be a function returning an integer, not a simple integer&lt;/p&gt;

&lt;p&gt;and finally a function that returns an array of integers where the size of the array is the number of bytes in the header and each element of the array is the byte at that offset (&lt;em&gt;in other words, it’s a byte array!&lt;/em&gt;) -&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolFrameValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// TODO: fill fv with header bytes&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We first create an object with the values that we want in the MPLS fields and then use some bit arithmetic to convert it to a 32-bit integer value using the header format encoding that we saw earlier  -&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolFrameValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12701&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// TODO: convert tagVal to header bytes and fill fv &lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We complete the function by extracting each byte out of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tagVal&lt;/code&gt; integer and populating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fv&lt;/code&gt; with it -&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolFrameValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12701&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We need one last thing - tell our preceding protocol &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Eth II&lt;/code&gt; (of which we are a payload) what our ethertype is  -&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x8847&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Putting all that together, here’s the full script -&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MPLS&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolFrameSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolFrameValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12701&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x8847&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Copy paste the above script into the user script editor, and hit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Compile&lt;/code&gt; (make sure the compilation results in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Success&lt;/code&gt;) -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-us-mpls-userscript.png&quot; alt=&quot;UserScript&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To preview the result of our script, go to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Packet View&lt;/code&gt; tab and click on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MPLS&lt;/code&gt; header in the top pane - the MPLS header bytes will be highlighted in the bottom pane -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-us-mpls-packet-view.png&quot; alt=&quot;Packet View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The generated 32-bit value is 0x0319DB40 which is the same value that we calculated manually in the &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-mpls-hexdump/&quot;&gt;previous post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To confirm that that the MPLS header is indeed correct, we apply our changes, transmit the stream, capture the transmitted packet and view it in Wireshark -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-us-mpls-wshark-single-tag.png&quot; alt=&quot;Wireshark MPLS single tag&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Voila, Wireshark decodes that perfectly!&lt;/p&gt;

&lt;p&gt;What if you need more than one tag?&lt;/p&gt;

&lt;p&gt;Simple -&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Change protocolFrameSize to return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n*4&lt;/code&gt; where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; is the number of tags that we want&lt;/li&gt;
  &lt;li&gt;Convert the single tag object to a list of tag objects&lt;/li&gt;
  &lt;li&gt;Size the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fv&lt;/code&gt; array to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n*4&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Do the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tagVal&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fv&lt;/code&gt; calculation in a loop&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s an example for 3 tags -&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MPLS&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolFrameSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolFrameValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12701&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;972&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocolId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x8847&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the 3-tag stack mpls frame in Wireshark -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-us-mpls-wshark-tag-stack.png&quot; alt=&quot;Wireshark MPLS tag stack&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ostinato userscript has much more capabilities than we saw in this post. See the &lt;a href=&quot;https://userguide.ostinato.org/user-script/&quot;&gt;UserScript Reference Documentation&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Interested in more Ostinato tips and tricks? &lt;a href=&quot;#subscribe&quot;&gt;Subscribe&lt;/a&gt; to receive email updates!&lt;/em&gt;&lt;/p&gt;
</description>
          <pubDate>Wed, 25 Apr 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-mpls-userscript/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-mpls-userscript/</guid>
        </item>
    
  
    
	<item>
          <title>Lichner - Lily</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A rendition of &lt;a href=&quot;https://en.wikipedia.org/wiki/Heinrich_Lichner&quot;&gt;Heinrich Lichner&lt;/a&gt;’s Opus 160 No. 6 - Lily&lt;/p&gt;

&lt;audio controls=&quot;&quot;&gt;
  &lt;source src=&quot;https://srivatsp.com/assets/sounds/lichner-lily.mp3&quot; type=&quot;audio/mpeg&quot; /&gt;
  Sorry, your browser doesn&apos;t support HTML5 audio
&lt;/audio&gt;

&lt;p&gt;Composer: Heinrich Lichner&lt;br /&gt;
Piano: Srivats P.&lt;/p&gt;

&lt;p&gt;Recorded on phone.&lt;/p&gt;
</description>
          <pubDate>Mon, 16 Apr 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/lichner-lily/</link>
          <guid isPermaLink="true">https://srivatsp.com/lichner-lily/</guid>
        </item>
    
  
    
	<item>
          <title>How to generate MPLS packets using Ostinato HexDump</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; includes and supports a lot of the commonly used protocols to generate packets. However, from time to time, one may come across a protocol that is not available in Ostinato.&lt;/p&gt;

&lt;p&gt;Fear not, Ostinato provides a way to generate packets for protocols that are not available out of the box. Actually it provides two ways, not one.&lt;/p&gt;

&lt;p&gt;In this post we will see how to create a MPLS packet by using the first of the two ways - hexdump.&lt;/p&gt;

&lt;p class=&quot;notice--info&quot;&gt;Although this post talks about MPLS, the same method is applicable for any protocol, MPLS is used just as an example here&lt;/p&gt;

&lt;p&gt;Let’s look at the MPLS frame format. Let’s assume we need to generate a IP/UDP packet encapsulated inside a MPLS tunnel. So the frame format would consist of the following protocol headers in this order -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+----------+------+----+-----+
| Ethernet | MPLS | IP | UDP |
+----------+------+----+-----+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All the protocols in the above frame except MPLS are already supported in Ostinato.&lt;/p&gt;

&lt;p&gt;To create this packet in Ostinato, we create a stream and select the protocols - Ethernet II, IPv4, UDP, Hex Dump and then switch to the advanced protocol selection mode -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-mpls-simple-proto.png&quot; alt=&quot;Simple Protocols&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HexDump&lt;/code&gt; protocol in the &lt;em&gt;Selected Protocols&lt;/em&gt; list and use the &lt;em&gt;up/down arrows&lt;/em&gt; to move it between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Eth II&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPv4&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-mpls-adv-proto.png&quot; alt=&quot;Advanced Protocols&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now that we have the HexDump header at the correct place in the packet, let’s switch to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Protocol Data&lt;/code&gt; tab and click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HexDump&lt;/code&gt; -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-mpls-proto-data.png&quot; alt=&quot;Protocol Data&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We need to enter the MPLS header bytes here in hex format. To calculate these, we need to know the MPLS header format.&lt;/p&gt;

&lt;p&gt;The MPLS header consists of one or more MPLS tags comprising a &lt;em&gt;tag stack&lt;/em&gt; -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+-------+-------+-------+-----+
| Tag 1 | Tag 2 | Tag 3 | ... |
+-------+-------+-------+-----+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each &lt;em&gt;tag&lt;/em&gt; is 32-bit wide and is composed of the following fields -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+------------+-----+-----+-----+
| MPLS Label | Exp |  S  | TTL |
|    (20)    | (3) | (1) | (8) |
+------------+-----+-----+-----+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;The numbers in () represents the bit-width of each field. The S bit is set to 1 for the last (innermost) tag and is 0 for all the other tags&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s create a single tag MPLS header with the following values -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Label = 12701
EXP = 5
S = 1
TTL = 64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First, let’s convert each of those values to hexadecimal -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Label = 12701 (0x319D)
EXP = 5 (0x5)
S = 1 (0x1)
TTL = 64 (0x40)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Looking at the tag encoding above and some bit arithmetic, we can now calculate the 32-bit hex encoded tag for these values -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0x0319DB40
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, we enter these bytes in the hexdump editor, and uncheck the padding -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-mpls-hexdump.png&quot; alt=&quot;HexDump&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We need to do one last thing - go to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ethernet II&lt;/code&gt; tab, tick the checkbox and enter the MPLS ethertype &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8847&lt;/code&gt; -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-mpls-ethtype.png&quot; alt=&quot;Ethertype&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To verify if the MPLS header is correct, we apply our changes, transmit the stream, capture the transmitted packet and view it in Wireshark -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-mpls-wshark-single-tag.png&quot; alt=&quot;Wireshark MPLS single tag&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Voila, Wireshark decodes that perfectly!&lt;/p&gt;

&lt;p&gt;What if you need more than one tag? Just calculate the 4-byte (32-bit) hex value for each tag (&lt;em&gt;make sure S=0 in all tags except the last one&lt;/em&gt;) and enter the same in the hexdump.&lt;/p&gt;

&lt;p&gt;Here’s an example -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Tag 1:
Label = 12701 (0x319D)
EXP = 5 (0x5)
S = 0 (0x0)
TTL = 64 (0x40)

Tag 2:
Label = 972 (0x3CC)
EXP = 1 (0x1)
S = 0 (0x0)
TTL = 128 (0x80)

Tag 3:
Label = 19 (0x13)
EXP = 3 (0x3)
S = 1 (0x1)
TTL = 255 (0xff)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hex Encoded value -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0319DA40 003CC280 000137FF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the 3-tag stack mpls frame in Wireshark -&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-mpls-wshark-tag-stack.png&quot; alt=&quot;Wireshark MPLS tag stack&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the next post, we’ll see how to use a Ostinato userscript to do the same thing so that we don’t need to do all this manual hex calculation which is prone to errors.&lt;/p&gt;

&lt;p&gt;Interested in more Ostinato tips and tricks? &lt;a href=&quot;#subscribe&quot;&gt;Subscribe&lt;/a&gt; to receive email updates!&lt;/p&gt;
</description>
          <pubDate>Wed, 11 Apr 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-mpls-hexdump/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-mpls-hexdump/</guid>
        </item>
    
  
    
	<item>
          <title>12 ways to mindful living</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/12-ways-to-mindful-living.png&quot; alt=&quot;12 ways to mindful living&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Download A4 size printable poster &lt;a href=&quot;https://srivatsp.com/assets/images/12-ways-to-mindful-living.pdf&quot;&gt;PDF&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: &lt;a href=&quot;http://zenhabits.net/toolset/&quot;&gt;Zen Habits&lt;/a&gt;&lt;br /&gt;
Design H/T: Paul Zii’s &lt;a href=&quot;https://www.skmurphy.com/blog/2014/01/17/ten-from-paul-zappias-29-ways-to-stay-creative/&quot;&gt;29 ways to stay creative&lt;/a&gt; (Paul’s original tumblr is dead)&lt;/em&gt;&lt;/p&gt;
</description>
          <pubDate>Tue, 13 Mar 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/mindful-living/</link>
          <guid isPermaLink="true">https://srivatsp.com/mindful-living/</guid>
        </item>
    
  
    
	<item>
          <title>How to change Ostinato&apos;s look and style</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;Ostinato uses &lt;a href=&quot;https://www.qt.io&quot;&gt;Qt&lt;/a&gt; for cross-platform UI functionality and ships with a few styles built in that mimic the look and feel of various platforms. You can use these styles without having to change or recompile the code.&lt;/p&gt;

&lt;p&gt;Use the command line param &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-style &amp;lt;style&amp;gt;&lt;/code&gt; when invoking ostinato. The available styles are -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;motif&lt;/li&gt;
  &lt;li&gt;cde&lt;/li&gt;
  &lt;li&gt;windows&lt;/li&gt;
  &lt;li&gt;cleanlooks&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;gtk&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;mac&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;plastique&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;windowsxp&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;windowsvista&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;notice--info&quot;&gt;The styles in &lt;em&gt;italics&lt;/em&gt; are fully supported only on their native OS due to their dependency on OS specific modules and libraries&lt;/p&gt;

&lt;p&gt;Here’s a sample gallery of a few styles for Ostinato running on Windows 10 (&lt;em&gt;click on the thumbnails for full image and description&lt;/em&gt;) -&lt;/p&gt;

&lt;figure class=&quot;half&quot;&gt;
  &lt;a href=&quot;https://srivatsp.com/assets/images/ost-style-win.png&quot; title=&quot;Windows 10 native&quot;&gt;
    &lt;img src=&quot;https://srivatsp.com/assets/images/thumb-ost-style-win.png&quot; /&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://srivatsp.com/assets/images/ost-style-plastique.png&quot; title=&quot;Plastique (KDE) style on Windows 10&quot;&gt;
    &lt;img src=&quot;https://srivatsp.com/assets/images/thumb-ost-style-plastique.png&quot; /&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://srivatsp.com/assets/images/ost-style-motif.png&quot; title=&quot;Motif (Go retro!) on Windows 10&quot;&gt;
    &lt;img src=&quot;https://srivatsp.com/assets/images/thumb-ost-style-motif.png&quot; /&gt;
  &lt;/a&gt;
  &lt;figcaption&gt;Ostinato style samples (on Windows 10)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;While it’s mostly just fun playing with the different looks, it might also be useful if you are using Ostinato on a OS that you normally don’t use.&lt;/p&gt;

&lt;p&gt;For example, if you are a Windows user and the Ubuntu Unity GUI throws you off, you could get back your familiar look and feel for Ostinato (and any other Qt4 app for that matter) by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-style windows&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here’s a sample gallery of a few styles for Ostinato running on Ubuntu 16.04 (&lt;em&gt;click on the thumbnails for full image and description&lt;/em&gt;) -&lt;/p&gt;

&lt;figure class=&quot;half&quot;&gt;
  &lt;a href=&quot;https://srivatsp.com/assets/images/ost-style-ubuntu.png&quot; title=&quot;Ubuntu 16.04 native&quot;&gt;
    &lt;img src=&quot;https://srivatsp.com/assets/images/thumb-ost-style-ubuntu.png&quot; /&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://srivatsp.com/assets/images/ost-style-windows.png&quot; title=&quot;Windows style on Ubuntu 16.04&quot;&gt;
    &lt;img src=&quot;https://srivatsp.com/assets/images/thumb-ost-style-windows.png&quot; /&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://srivatsp.com/assets/images/ost-style-cde.png&quot; title=&quot;CDE style on Ubuntu 16.04&quot;&gt;
    &lt;img src=&quot;https://srivatsp.com/assets/images/thumb-ost-style-cde.png&quot; /&gt;
  &lt;/a&gt;
  &lt;figcaption&gt;Ostinato style samples (on Ubuntu 16.04)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;That purple CDE window brings back memories of my first job where we had Sun Solaris workstations running CDE!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Interested in more Ostinato tips and tricks? &lt;a href=&quot;#subscribe&quot;&gt;Subscribe&lt;/a&gt; to receive email updates!&lt;/p&gt;
</description>
          <pubDate>Tue, 06 Mar 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-styles/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-styles/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato for Linux Maximum Transmit Rate</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p class=&quot;notice--info&quot;&gt;&lt;strong&gt;Update (2021)&lt;/strong&gt;: Ostinato now supports 10G, 25G and 40Gbps line-rate rate traffic using the &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;Turbo Transmit&lt;/a&gt; add-on.&lt;/p&gt;

&lt;p&gt;I’ve been intermittently posting results for the maximum transmit rate performance test with Ostinato 0.9. See results for &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-live-iso-maximum-transmit-rate/&quot;&gt;Live ISO&lt;/a&gt;, &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-macos-maximum-transmit-rate/&quot;&gt;MacOS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we do the same test on Linux - specifically Ubuntu 16.04.3 LTS. I expected the results to be same as the Live ISO since it is also Linux based, but &lt;em&gt;&lt;strong&gt;I was in for a surprise!&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I configured the same single stream as earlier -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Protocols: Mac, Ethernet II, IPv4, UDP, Pattern&lt;/li&gt;
  &lt;li&gt;Source and Destination Mac addresses populated with the actual Mac addresses of the source and sink&lt;/li&gt;
  &lt;li&gt;Source and Destination IP addresses populated with the actual IP addresses of the source and sink&lt;/li&gt;
  &lt;li&gt;Packets/sec: 0&lt;/li&gt;
  &lt;li&gt;Number of packets: 10 (default)&lt;/li&gt;
  &lt;li&gt;After this stream: Goto first&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;performance-figures&quot;&gt;Performance figures&lt;/h1&gt;
&lt;p&gt;Here are the numbers -&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Packet Size&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Max Rate (Kpps)&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Max Rate (Mbps)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;64&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;920&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;618&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;128&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;807&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;955&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;256&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;442&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;976&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;512&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;231&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;983&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1024&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;118&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;986&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1518&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;80&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;984&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Mbps rate was calculated using the below formula to take into account the ethernet line overhead of 20 bytes (1 byte SFD + 7 byte preamble + 12 byte inter-packet gap) -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Rate (in Mbps) = (PacketSize + 20) * kppsRate * 8 / 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;specs&quot;&gt;Specs&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Software&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://ostinato.gumroad.com/l/ostlnx09&quot;&gt;Ostinato 0.9 - for Linux (Ubuntu 16.04, 64-bit)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Ubuntu 16.04.3 LTS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Processor: Xeon E5-2620v4 @ 2.10GHz (8-core)&lt;/li&gt;
  &lt;li&gt;RAM: 32GB&lt;/li&gt;
  &lt;li&gt;NIC: Intel I218-V 1Gbps&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;observations&quot;&gt;Observations&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;Although it’s a 8-core CPU, only one core reaches 100% CPU during the test (same as Live ISO) - as expected since for a single port, Ostinato doesn’t use more than one core&lt;/li&gt;
  &lt;li&gt;RAM usage - 26MB (value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RES&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;top&lt;/code&gt;); this is lower than the 98MB used in the LiveISO&lt;/li&gt;
  &lt;li&gt;Max line rate (well - quite close to it) is reached for 256 byte packets and larger, same as the Live ISO and MacOS.&lt;/li&gt;
  &lt;li&gt;For smaller packet sizes, Ubuntu performs better &lt;em&gt;(64 - 618Mbps, 128 - 955Mbps)&lt;/em&gt; than the Linux ISO &lt;em&gt;(64 - 403Mbps, 128 - 702Mbps)&lt;/em&gt; - in fact, the 128 byte rate is quite close to line rate&lt;/li&gt;
  &lt;li&gt;Speculating on what could cause this performance difference on the two different Linux distros -
    &lt;ul&gt;
      &lt;li&gt;The application code is exactly the same for both Ubuntu and LiveISO&lt;/li&gt;
      &lt;li&gt;In the packet transmit code path, there are no Qt APIs&lt;/li&gt;
      &lt;li&gt;The non application packet transmit code path is essentially composed of pcap_sendpacket() and the underlying system call(s) - confirmed by &lt;em&gt;perf&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;Version comparison of these components -&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Component&lt;/th&gt;
      &lt;th&gt;Ubuntu 16.04.3&lt;/th&gt;
      &lt;th&gt;Live ISO (TinyCore 8.2)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Kernel&lt;/td&gt;
      &lt;td&gt;4.13.0-32-generic (64-bit)&lt;/td&gt;
      &lt;td&gt;4.8.17-tinycore (32-bit)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;glibc&lt;/td&gt;
      &lt;td&gt;2.23&lt;/td&gt;
      &lt;td&gt;2.50.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;libpcap&lt;/td&gt;
      &lt;td&gt;1.7.4-2&lt;/td&gt;
      &lt;td&gt;1.4.0&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;For more Ostinato posts, &lt;a href=&quot;#subscribe&quot;&gt;subscribe&lt;/a&gt; for email updates.&lt;/p&gt;
</description>
          <pubDate>Mon, 26 Feb 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-ubuntu-maximum-transmit-rate/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-ubuntu-maximum-transmit-rate/</guid>
        </item>
    
  
    
	<item>
          <title>10 reasons why contributing to open source is better than an internship</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;College students often look for internships. Here in India, these are typically for 5-6 months (one semester - required for the degree) or 2 months (if done during the summer)&lt;/p&gt;

&lt;p&gt;While some organisations have limited slots available for the former - a lot of students still struggle to get one. The latter is actually quite difficult to find - mostly because there’s not much someone can get done in 2 months without costing the organisation a lot (in $ and time).&lt;/p&gt;

&lt;p&gt;I believe it is better for students to instead use the time to contribute to an open-source project, than go hunting for internship opportunities.&lt;/p&gt;

&lt;p&gt;All the skills that you will learn while working on an open-source project are directly transferrable to an organisation and will make you effective and productive much faster than your peers. And you know what they say about first impressions - &lt;em&gt;right?&lt;/em&gt; So why not jump-start your career by working on and contributing to an open-source project.&lt;/p&gt;

&lt;p&gt;Here’s how it will help -&lt;/p&gt;

&lt;h1 id=&quot;1-choice-of-project&quot;&gt;1. Choice of project&lt;/h1&gt;
&lt;p&gt;At most organisations, you won’t be given an option to choose the project. With open-source, you have a HUGE variety of projects to choose from. So why not choose a project/technology/language that &lt;em&gt;you&lt;/em&gt; are interested in? It could even be a tool or software that you already use!&lt;/p&gt;

&lt;h1 id=&quot;2-version-control&quot;&gt;2. Version Control&lt;/h1&gt;
&lt;p&gt;Version control in a professional setup - an organisation or an open source project - is critical and I doubt there’s any organisation that doesn’t have it in place. For open-source projects, it is the core part of the collaborative process. But college students typically don’t use or require it if all they do is short, throw-away programming assignments. Thankfully, this is beginning to change with the advent and popularity of GitHub and students are using this while in college itself.&lt;/p&gt;

&lt;h1 id=&quot;3-navigate-a-large-code-base&quot;&gt;3. Navigate a large code base&lt;/h1&gt;
&lt;blockquote&gt;
  &lt;p&gt;Sir, where can I find main() in the code?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A fresher once asked this question to a colleague of mine. He wanted to read the code starting from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main()&lt;/code&gt;. The question was honest and well-intended. But, as we know, it’s not possible to do this with most code bases that you get to work on in organisations because of the sheer size of the code base.&lt;/p&gt;

&lt;p&gt;You have to necessarily use tools like ctags, cscope, opengrok or IDEs to navigate around.&lt;/p&gt;

&lt;p&gt;A lot of open source projects’ code will also be reasonably big in size (at least compared to the short programs students tend to write in college), so it’s an opportunity to learn these tools and skills that will be relevant when you start your career.&lt;/p&gt;

&lt;h1 id=&quot;4-learn-to-find-and-isolate-areas-of-code&quot;&gt;4. Learn to find and isolate areas of code&lt;/h1&gt;
&lt;p&gt;Related to the previous, to troubleshoot and debug the first step is always to localize the area of code that you need to look at. And this is a skill that comes with practice - how to quickly identify the potential area of code to look at from amongst the ocean of code.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;must&lt;/strong&gt; skill in an open-source project or in an organisation.&lt;/p&gt;

&lt;h1 id=&quot;5-read-code-coz-theres-no-documentation&quot;&gt;5. Read code ‘coz there’s no documentation&lt;/h1&gt;
&lt;p&gt;Most organisations hopefully will have some documentation - spec, design etc. However, even if they do, code changes fast and documents almost never keep up. So you have to read the code as the single source of truth. How does a feature work? Read the code and figure out!&lt;/p&gt;

&lt;p&gt;Guess what? Open-source projects are no different - they have even less documentation than organisations. Get all the practice you can!&lt;/p&gt;

&lt;h1 id=&quot;6-community-interaction&quot;&gt;6. Community Interaction&lt;/h1&gt;
&lt;p&gt;When you try and contribute to open source, you may get feedback from the community. While most projects will try and be nice to you (&lt;em&gt;a lot of projects even have a code of conduct that requires this&lt;/em&gt;), you may still face brickbats or curt replies. That’s practice to develop a hard shell and not take it personally.&lt;/p&gt;

&lt;p&gt;A valuable skill in an organisation and in life.&lt;/p&gt;

&lt;h1 id=&quot;7-write-better---emails-and-docs&quot;&gt;7. Write better - emails and docs&lt;/h1&gt;
&lt;p&gt;While progressive organisations have probably moved on to Slack et.al., email is still prevalent and relevant. You need to learn how to write emails that communicate effectively but succintly. How to write a good subject line. Etiquettes regarding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to:&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cc:&lt;/code&gt;. Format the body of the email correctly e.g. using mono-width font for code/log snippets. End with a &lt;em&gt;call to action&lt;/em&gt; - indicating if something is expected from the recipients.&lt;/p&gt;

&lt;p&gt;Most interaction on open-source projects happens over the mailing list or in bug reports. So you get to practice your writing skills.&lt;/p&gt;

&lt;p&gt;As any writer will tell you, the only way to be a better writer is to write more. And that’s true for emails too! And as you will discover - being a good communicator is sometimes even more important than your programming chops.&lt;/p&gt;

&lt;h1 id=&quot;8-showcase-your-work-publicly&quot;&gt;8. Showcase your work publicly&lt;/h1&gt;
&lt;p&gt;When an internship ends what you have usually is a short section in your resume describing the project. But access to any docs, code etc. cannot be shared because it is proprietary. So any future recruiter has only that short blurb in the Resume to look at and make a judgement on.&lt;/p&gt;

&lt;p&gt;With open-source projects, all your work is in the open for anyone to view allowing them to make better and more informed judgements on your actual work. So if you did a good job and are proud of the work, showcase your work by including a link to the project and your contribution.&lt;/p&gt;

&lt;p&gt;In the US including your GitHub username in your Resume is almost required these days especially for entry level positions.&lt;/p&gt;

&lt;h1 id=&quot;9-gratitude-from-the-project-maintainer&quot;&gt;9. Gratitude from the project maintainer&lt;/h1&gt;
&lt;p&gt;Open-soure project maintainers are a harried lot, buried under ever-piling mountains of bug reports and feature requests.&lt;/p&gt;

&lt;p&gt;They will be thankful for any contributions that you make. Just be mindful of the fact that you may fix a bug or develop a feature for the project, but in all likelyhood they helped you by answering your (multiple) queries, reviewing your code etc. - all of them things that take non-trivial amounts of time - the one commodity that maintainers have in limited supply.&lt;/p&gt;

&lt;p&gt;The most important thing you can do to justify their time investment in you is #10&lt;/p&gt;

&lt;h1 id=&quot;10-continue-to-work-on-project&quot;&gt;10. Continue to work on project&lt;/h1&gt;
&lt;p&gt;Once the internship is over, you no longer have access to the code or organisation. Not so with open-source projects. You can continue to work on the same project - new bugs or new features and become a regular contributor to the project.&lt;/p&gt;

&lt;p&gt;Being a regular contributor is not just a big boost to your credibility (and Resume), but it also means you get into the habit of &lt;em&gt;Ship early, ship often&lt;/em&gt; - a trait that organisations love (but hinder with their process overhead)&lt;/p&gt;

&lt;p&gt;Convinced?&lt;/p&gt;

&lt;p&gt;Good!&lt;/p&gt;

&lt;p&gt;Where do I start, you ask?&lt;/p&gt;

&lt;p&gt;Start with &lt;a href=&quot;https://medium.freecodecamp.com/finding-your-first-open-source-project-or-bug-to-work-on-1712f651e5ba&quot;&gt;finding your first open source project or bug to work on&lt;/a&gt; or &lt;a href=&quot;https://opensourcefriday.com/&quot;&gt;open-source friday&lt;/a&gt;&lt;/p&gt;
</description>
          <pubDate>Wed, 07 Feb 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/open-source-vs-internship/</link>
          <guid isPermaLink="true">https://srivatsp.com/open-source-vs-internship/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato for MacOS Maximum Transmit Rate</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p class=&quot;notice--info&quot;&gt;&lt;strong&gt;Update (2021)&lt;/strong&gt;: Ostinato now supports 10G, 25G and 40Gbps line-rate rate traffic using the &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;Turbo Transmit&lt;/a&gt; add-on.&lt;/p&gt;

&lt;p&gt;In my last post, I presented results for a &lt;a href=&quot;https://srivatsp.com/ostinato/ostinato-live-iso-maximum-transmit-rate/&quot;&gt;maximum transmit rate performance test on the Ostinato 0.9 Live ISO&lt;/a&gt;. I intended to repeat that for all platforms, but life got in the way.&lt;/p&gt;

&lt;p&gt;I got some time today, so I did a test on MacOS with the same Ostinato 0.9 version as the Live ISO.&lt;/p&gt;

&lt;p&gt;I configured a single stream, again same as earlier -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Protocols: Mac, Ethernet II, IPv4, UDP, Pattern&lt;/li&gt;
  &lt;li&gt;Source and Destination Mac addresses populated with the actual Mac addresses of the source and sink&lt;/li&gt;
  &lt;li&gt;Source and Destination IP addresses populated with the actual IP addresses of the source and sink&lt;/li&gt;
  &lt;li&gt;Packets/sec: 0&lt;/li&gt;
  &lt;li&gt;Number of packets: 10 (default)&lt;/li&gt;
  &lt;li&gt;After this stream: Goto first&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;performance-figures&quot;&gt;Performance figures&lt;/h1&gt;
&lt;p&gt;Here are the numbers -&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Packet Size&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Max Rate (Kpps)&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Max Rate (Mbps)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;64&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;755&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;507&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;128&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;695&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;823&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;256&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;448&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;989&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;512&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;233&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;992&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1024&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;119&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;994&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1518&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;81&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;997&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Mbps rate was calculated using the below formula to take into account the ethernet line overhead of 20 bytes (1 byte SFD + 7 byte preamble + 12 byte inter-packet gap) -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Rate (in Mbps) = (PacketSize + 20) * kppsRate * 8 / 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;specs&quot;&gt;Specs&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Software&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://ostinato.gumroad.com/l/ostmac09&quot;&gt;Ostinato 0.9 - for MacOS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;macOS Sierra Version 10.12.6&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;MacBook Pro&lt;/li&gt;
  &lt;li&gt;Processor: Intel Core i7 @ 2.5GHz (4-core), Hyperthreading on&lt;/li&gt;
  &lt;li&gt;RAM: 16GB&lt;/li&gt;
  &lt;li&gt;Thunderbold to Gigabit Ethernet Adaptor&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;observations&quot;&gt;Observations&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;Max line rate (well - quite close to it) is reached for 256 byte packets and larger, same as the Live ISO&lt;/li&gt;
  &lt;li&gt;For smaller packet sizes, the MacOS version performs better &lt;em&gt;(64 - 507Mbps, 128 - 823Mbps)&lt;/em&gt; than the Linux ISO &lt;em&gt;(64 - 403Mbps, 128 - 702Mbps)&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Although the underlying hardware and OS is different, I &lt;em&gt;think&lt;/em&gt; the primary reason for the better performance is because of the OS - with Linux, only one core reaches 100% CPU during the test, but with MacOS (which is based on BSD) two processes show usage &amp;gt;= 100% - &lt;em&gt;drone&lt;/em&gt; and &lt;em&gt;kernel_task&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Looking at per core CPU usage/history in activity monitor, 4 (out of 8 vCPUs) show usage between 50 to 60%&lt;/li&gt;
  &lt;li&gt;I can’t seem to figure out how to see per thread CPU usage or the associated core for a thread to dissect futher - (&lt;em&gt;if you know how to get this info, please share in the comments below&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Thu, 01 Feb 2018 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-macos-maximum-transmit-rate/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-macos-maximum-transmit-rate/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato Live ISO Maximum Transmit Rate</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p class=&quot;notice--info&quot;&gt;&lt;strong&gt;Update (2021)&lt;/strong&gt;: Ostinato now supports 10G, 25G and 40Gbps line-rate rate traffic using the &lt;a href=&quot;https://ostinato.org/pricing/turbo&quot;&gt;Turbo Transmit&lt;/a&gt; add-on.&lt;/p&gt;

&lt;p&gt;As part of the Ostinato 0.9 release, I also packaged Ostinato as a &lt;a href=&quot;https://ostinato.org/pricing/&quot;&gt;Virtual appliance&lt;/a&gt; and a &lt;a href=&quot;https://ostinato.org/pricing/&quot;&gt;Live ISO&lt;/a&gt;. I wanted to check the maximum packet generation rate performance of these form factors, so I used &lt;a href=&quot;https://rufus.akeo.ie&quot;&gt;Rufus&lt;/a&gt; to burn the Live ISO to a USB pendrive and booted off the USB to collect the numbers.&lt;/p&gt;

&lt;p&gt;I configured a single stream as given below -&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Protocols: Mac, Ethernet II, IPv4, UDP, Pattern&lt;/li&gt;
  &lt;li&gt;Source and Destination Mac addresses populated with the actual Mac addresses of the source and sink&lt;/li&gt;
  &lt;li&gt;Source and Destination IP addresses populated with the actual IP addresses of the source and sink&lt;/li&gt;
  &lt;li&gt;Packets/sec: 0&lt;/li&gt;
  &lt;li&gt;Number of packets: 10 (default)&lt;/li&gt;
  &lt;li&gt;After this stream: Goto first&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;performance-figures&quot;&gt;Performance figures&lt;/h1&gt;
&lt;p&gt;Here are the numbers -&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Packet Size&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Max Rate (Kpps)&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Max Rate (Mbps)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;64&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;600&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;403&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;128&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;593&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;702&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;256&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;442&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;976&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;512&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;231&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;983&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1024&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;118&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;986&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1518&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;80&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;984&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Mbps rate was calculated using the below formula to take into account the ethernet line overhead of 20 bytes (1 byte SFD + 7 byte preamble + 12 byte inter-packet gap) -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Rate (in Mbps) = (PacketSize + 20) * kppsRate * 8 / 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;specs&quot;&gt;Specs&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Software&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://gum.co/ostiso09&quot;&gt;Ostinato 0.9 Live ISO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Processor: Xeon E5-2620v4 @ 2.10GHz (8-core)&lt;/li&gt;
  &lt;li&gt;RAM: 32GB&lt;/li&gt;
  &lt;li&gt;NIC: Intel I218-V 1Gbps&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;observations&quot;&gt;Observations&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Although it’s a 8-core CPU, only one core reaches 100% CPU during the test - this is expected since for a single port, Ostinato doesn’t use more than one core&lt;/li&gt;
  &lt;li&gt;RAM usage - 98MB&lt;/li&gt;
  &lt;li&gt;Max traffic rate for 64 byte packets is only 403Mbps&lt;/li&gt;
  &lt;li&gt;Max line rate (well - quite close to it) is reached for 256 byte packets and larger, but it never reaches 1000 Mbps even for larger packet size (i.e. lower Kpps rate) - I wonder why. It can’t be related to Ostinato itself, but something system related (&lt;em&gt;if you have any thoughts on this, please share in the comments below&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Fri, 29 Dec 2017 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/ostinato-live-iso-maximum-transmit-rate/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/ostinato-live-iso-maximum-transmit-rate/</guid>
        </item>
    
  
    
	<item>
          <title>Señor Santa Claus (cover)</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A cover of Jim Reeves’ &lt;em&gt;Señor Santa Claus&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Merry Christmas and Happy Holidays!&lt;/p&gt;

&lt;audio controls=&quot;&quot;&gt;
  &lt;source src=&quot;https://srivatsp.com/assets/sounds/senor-santa-claus-cover.mp3&quot; type=&quot;audio/mpeg&quot; /&gt;
  Sorry, your browser doesn&apos;t support HTML5 audio
&lt;/audio&gt;

&lt;p&gt;Piano and vocals: Srivats P.&lt;/p&gt;

&lt;p&gt;Recorded on phone.&lt;/p&gt;
</description>
          <pubDate>Mon, 25 Dec 2017 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/senor-santa-claus-cover/</link>
          <guid isPermaLink="true">https://srivatsp.com/senor-santa-claus-cover/</guid>
        </item>
    
  
    
	<item>
          <title>Brown Boy</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A good friend of mine recently published a Kindle short - &lt;a href=&quot;http://amzn.to/2zs6hMj&quot;&gt;Brown Boy&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hitesh lost his son in these Himalayan mountains years ago. They tried to tell him it was a wild animal that took the child on a stormy night. But Hitesh remembers the scratching sound outside his window that night. He remembers the door that yawned open by itself. Most of all, he remembers the story of the mysterious creature of legend said to haunt these dark jungles. The one everyone is afraid to talk about …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I designed the cover for the book based on a photograph by &lt;a href=&quot;https://pixabay.com/en/users/jodylehigh-28951/&quot;&gt;Jody Davis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/2zs6hMj&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/BrownBoyCover1.6.png&quot; alt=&quot;Brown Boy&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cover Photo: &lt;a href=&quot;https://pixabay.com/en/users/jodylehigh-28951/&quot;&gt;Jody Davis&lt;/a&gt;&lt;br /&gt;
Cover Design: Srivats P&lt;/p&gt;
</description>
          <pubDate>Wed, 08 Nov 2017 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/brown-boy/</link>
          <guid isPermaLink="true">https://srivatsp.com/brown-boy/</guid>
        </item>
    
  
    
	<item>
          <title>Hey There Delilah (cover)</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;A cover of Plain White T’s &lt;em&gt;Hey There Delilah&lt;/em&gt;&lt;/p&gt;

&lt;audio controls=&quot;&quot;&gt;
  &lt;source src=&quot;https://srivatsp.com/assets/sounds/hey-there-delilah-cover.mp3&quot; type=&quot;audio/mpeg&quot; /&gt;
  Sorry, your browser doesn&apos;t support HTML5 audio
&lt;/audio&gt;

&lt;p&gt;Piano and vocals: Srivats P.&lt;/p&gt;

&lt;p&gt;Recorded on phone.&lt;/p&gt;
</description>
          <pubDate>Sat, 14 Oct 2017 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/hey-there-delilah-cover/</link>
          <guid isPermaLink="true">https://srivatsp.com/hey-there-delilah-cover/</guid>
        </item>
    
  
    
	<item>
          <title>Ostinato first run UX improvements</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;About a month ago, Ostinato user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cube_030&lt;/code&gt; provided the following suggestions as part of the &lt;a href=&quot;https://www.surveymonkey.com/results/SM-3CJQP5M88/&quot;&gt;Ostinato quick feedback survey&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;UI needs to be cleaned up some things should be automatic or more clear.
One example is after loading a stream it needs to be applied. This was not clear at first.&lt;br /&gt;
Also some buttons are available even if they do nothing. i.e. when no interface is selected you can still hit play.&lt;br /&gt;
However this product has helped a lot and made testing a lab work much easier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Around the same time I subscribed to &lt;a href=&quot;https://www.kalzumeus.com&quot;&gt;Patrick McKenzie (patio11)&lt;/a&gt; and the first email/video in the series talked about improving the first run experience for users.&lt;/p&gt;

&lt;p&gt;I’ve always known that the Ostinato UI is not intuitive for first timers - especially those who haven’t used commercial traffic generators. The &lt;a href=&quot;https://userguide.ostinato.org/architecture/&quot;&gt;split architecture&lt;/a&gt; and the notion of &lt;a href=&quot;https://userguide.ostinato.org/streams/&quot;&gt;streams&lt;/a&gt; etc. are unfamiliar to them. But for the longest time I’ve been reluctant to simplify the interface hoping instead that a &lt;a href=&quot;https://userguide.ostinato.org/quick-start/&quot;&gt;quickstart&lt;/a&gt; will be able to bridge the gap.&lt;/p&gt;

&lt;p&gt;Watching Patrick’s video (available only to subscribers) convinced me that this was a missed opportunity. They will most likely open the UI, not know what to do, click a few things here and there, things won’t work and they will close the app in frustration regretting their decision to try it out.&lt;/p&gt;

&lt;p&gt;So I decided to do something about it. Patrick recommends the first run of the app to start with a virtual tour of the interface and how to perform common tasks. I couldn’t figure out how to do this in Ostinato which is a &lt;a href=&quot;https://www.qt.io&quot;&gt;Qt widgets&lt;/a&gt; based desktop app (&lt;em&gt;modal dialogs with customized placement maybe?&lt;/em&gt;) - I decided not to spend too much time figuring this out, but see if there was some other way I could achieve the same objective.&lt;/p&gt;

&lt;p&gt;I started with the first screen that users see when they run the app and put myself in the shoes of a new user -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/welcome-screen-before.png&quot; alt=&quot;Welcome Screen (before)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A new user’s reaction to that is likely to be -&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What am I supposed to do here? How do I generate a packet?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, I wrote some helpful text utilizing the empty top right area. This area is used only when you select a port from the port list. However, when the app opens, nothing is selected in the port list, so it remains blank. A good space for basic instructions -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/welcome-screen-draft-a.png&quot; alt=&quot;Welcome Screen (draft A)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s better.&lt;/p&gt;

&lt;p&gt;The challenge with writing instructional text is - you want it to be succint, not just because space is constrained and visually you don’t want it to be crowded, but because longer the text, less likely the user will read it. Writing the copy for this (and for subsequent textual hints) took me several iterations, tweaks and was quite time consuming, but I told myself - &lt;em&gt;good copy takes time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What would be even better was if the port group auto expanded to show all the ports instead of the user having to double-click the port group to reveal it (&lt;em&gt;I remember wanting to do this when I first wrote this code but that didn’t happen for some reason - maybe ‘coz I was new to Qt and/or I had other important stuff to implement, so I probably didn’t spend much time on it&lt;/em&gt;). Anyway, I figured it out this time -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/welcome-screen-draft-b.png&quot; alt=&quot;Welcome Screen (draft B)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One problem I ran into was that once a port group or a port is selected in that list causing this welcome screen to be replaced, there’s no way to see it again. There will always be a current/selected item in that list. A bit of research and I found that this is a &lt;a href=&quot;https://stackoverflow.com/questions/2761284/is-it-possible-to-deselect-in-a-qtreeview-by-clicking-off-an-item&quot;&gt;QTreeView bug/behaviour&lt;/a&gt; - so I wrote &lt;a href=&quot;https://github.com/pstavirs/ostinato/commit/abb48a1c12cacbb0132d9d1cc25ff95216ddb459#diff-3f66c7d2ba3e6d61a0c4d0504089b362&quot;&gt;XTreeView&lt;/a&gt; to fix that.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But how would a user know that clicking on a blank area in the port list would bring back the welcome screen?&lt;/em&gt; Well, I don’t have an answer for that - I can just hope that if he clicks around, he will discover it :-)&lt;/p&gt;

&lt;p&gt;Another thing I wanted to tackle here was a common problem that users run into when they first run Ostinato - the port group is empty. I have a &lt;a href=&quot;https://ostinato.org/docs/faq/#q-port-group-has-no-interfaces&quot;&gt;detailed entry in the FAQ&lt;/a&gt; on why this happens and how to fix this - but a first time user doesn’t know that!&lt;/p&gt;

&lt;p&gt;So, I added some text to point to that.&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/welcome-screen-after.png&quot; alt=&quot;Welcome Screen (after)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The instructions on this welcome page asks the user to select the port and hopefully that’s what he would do, but what if selects a port group?&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/port-group-screen-before.png&quot; alt=&quot;Port Group Screen (before)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can do better than that. Let’s reinforce the instructions by repeating them -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/port-group-screen-after.png&quot; alt=&quot;Port Group Screen (draft A)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s select a port.&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/port-screen-before.png&quot; alt=&quot;Port Screen (before)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We run into a problem here. We no longer have a blank space but a widget here, so how do we write some instructions? I tried adding instructional text at the bottom of the stream list widget, but didn’t like that it took up valuable screen estate and reduced the size of the stream list widget. I tried adding the text next to the Apply button, which solved the screen estate problem, but wasn’t entirely satisfied with the results -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/port-screen-draft-a.png&quot; alt=&quot;Port Screen (draft A)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;What if we could reuse the blank area of the stream list widget itself? We need the instructions only to add the first stream and hopefully user would remember those to add more streams.&lt;/p&gt;

&lt;p&gt;So I wrote &lt;a href=&quot;https://github.com/pstavirs/ostinato/commit/4d13ecf15d149ca3a3e84ce49805e49347e20893#diff-ca327529497d2cd0eebd884302dde511&quot;&gt;XTableView&lt;/a&gt; which displays some helpful text if the view has no items to be displayed. I utilized the area next to the Apply button to hint at when clicking Apply was required.&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/port-screen-after.png&quot; alt=&quot;Port Screen (after)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So the user right-clicks and selects New Stream from the context menu to create his first stream. Great!&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/stream-list.png&quot; alt=&quot;Stream List&quot; /&gt;&lt;/p&gt;

&lt;p&gt;What’s the next step? He needs to edit the just created stream because add just creates a &lt;em&gt;default&lt;/em&gt; stream, not with the attributes that the user wants.&lt;/p&gt;

&lt;p&gt;So what does the user have to do to edit the stream? Double click on the gear icon in the stream or right click the stream to access the context menu. Not very intuitive from the above screen. The user has had a glimpse of the context menu when adding a new stream, so he &lt;em&gt;might&lt;/em&gt; have seen that it included an edit stream action also. Not something we can rely on.&lt;/p&gt;

&lt;p&gt;Hold on. Do we really need to have a 2 step new+edit action? What if we collapse it into a single action? i.e. new stream would directly open the stream configuration dialog (used to edit the stream)?&lt;/p&gt;

&lt;p&gt;A good idea, but we need to also handle these cases -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Include stream name and stream enable/disable in the dialog - the only way to edit these stream fields currently was directly from the stream list, not from the dialog&lt;/li&gt;
  &lt;li&gt;Adding multiple new streams requires the ability for the dialog to support editing and traversing multiple streams including the ability to identify which stream he is editing&lt;/li&gt;
  &lt;li&gt;Adding one or more streams and then pressing Cancel in the dialog should not incorrectly mark the port as dirty (more on this later)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Making these changes in the code required some refactoring and rework, but at last it was done -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/scd-after.png&quot; alt=&quot;Stream Config Dialog (after)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next step? Click Apply to push changes to the agent. A frequent stumbling block for new users who don’t realize that without doing that no packets will go out.&lt;/p&gt;

&lt;p&gt;Ideally, we should auto apply any changes, but ‘coz applying changes is a sequence of synchronous RPCs, they sometimes take time and the UI is disabled for that duration. Which is a irritant if you have to create/edit multiple streams. So I had taken the original design decision to not do a auto apply and leave it to the user to decide when to apply.&lt;/p&gt;

&lt;p&gt;There was also a matter of being able to detect all the changes made by the user to mark the port as dirty - not a straight forward task.&lt;/p&gt;

&lt;p&gt;As with any hard task I had a lot of resistance to tackle this. But somehow this time I managed to overcome that and got down to the task of detecting all the changes that make a port dirty. In the end, it wasn’t as difficult as I thought it would be (&lt;em&gt;note to self: everything is not as difficult as it appears to be&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Once I was able to detect when a port was dirty and needed to be &lt;em&gt;applied&lt;/em&gt;, I replaced the always on generic text I had added next to the Apply button to be intelligent. The text would appear only when a change made the port dirty. I also changed the color of port in the port list to red to signal to the user that something requires his attention.&lt;/p&gt;

&lt;p&gt;To show that these are related, I used a bit of red in the apply text to match the port name.&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/apply-text.png&quot; alt=&quot;Apply text&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ok, user hits apply. Next step?&lt;/p&gt;

&lt;p&gt;He needs to go over to the Port Statistics window for the next task - stream transmit. So once Apply is clicked, we change the Apply text to direct him there -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/apply-done-text.png&quot; alt=&quot;Apply done text&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Trigerring stream transmit in the port statistics window is a multi-step process -&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Select the port(s)&lt;/li&gt;
  &lt;li&gt;Click on the Start Transmit toolbar button&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/port-stats-window-before.png&quot; alt=&quot;Port Stats Window (before)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cube_030&lt;/code&gt; pointed out in his suggestions, ‘coz the toolbar buttons are always enabled, it is not clear that user first needs to select a port.&lt;/p&gt;

&lt;p&gt;Also, because the buttons have only icons, no text, you don’t know which is the transmit button (&lt;em&gt;they do have tooltips which appear when you hover over the button, but that’s not intuitive&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;I tried adding text to all the buttons but they took up too much screen estate, so I added category labels instead.&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/port-stats-window-draft-a.png&quot; alt=&quot;Port Stats Window (draft a)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For some reason that I don’t recall now, to select a port in this window, I require that the user select entire port column by clicking on the column header - selecting a few cells of the port wouldn’t suffice. The user guide even has a &lt;a href=&quot;https://userguide.ostinato.org/port-statistics/&quot;&gt;helpful image&lt;/a&gt; to warn about this. But a first time user doesn’t know that.&lt;/p&gt;

&lt;p&gt;So, I changed the code to automatically select the entire column when you click anywhere in that column, not just the header. I also changed the code so that buttons are enabled only if one or more ports are selected -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/port-stats-window-after.png&quot; alt=&quot;Port Stats Window (after)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The user can now click on the Transmit play button to start transmitting packets.&lt;/p&gt;

&lt;p&gt;Here’s an animated walkthrough the new UX -&lt;/p&gt;

&lt;p class=&quot;full&quot;&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/ost-ux-first-run/first-run-ux.gif&quot; alt=&quot;UX Creating and transmitting a stream&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To be consistent, I also added similar hints as the stream list to the device list as well, although more needs to be done there. I’ve left that for a future release.&lt;/p&gt;

&lt;p&gt;With these UX improvements, users will hopefully have a better first (and subsequent) run experience.&lt;/p&gt;

&lt;p&gt;For good measure, I added/improved some error messages for frequent stumbling blocks that users face - that’s the next post though!&lt;/p&gt;

&lt;p&gt;All these UX improvements will be part of 0.9 release which I’m currently working towards.&lt;/p&gt;
</description>
          <pubDate>Sun, 01 Oct 2017 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/first-run-ostinato-ux-improvements/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/first-run-ostinato-ux-improvements/</guid>
        </item>
    
  
    
	<item>
          <title>No qDebug output with Qt 4.8.7 and MinGW</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;When setting up my Qt based development environment for &lt;a href=&quot;https://ostinato.org/&quot;&gt;Ostinato&lt;/a&gt;, on my new Win 10 based desktop, I ran into this strange problem where the qDebug output wouldn’t be available/visible on &lt;a href=&quot;https://docs.microsoft.com/en-us/sysinternals/downloads/debugview&quot;&gt;DbgView&lt;/a&gt;. After several frustrating days of messing around with various debuggers and alternatives to DbgView, it appears that the problem was in Qt itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Qt installation had setup &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win32-g++-4.6&lt;/code&gt; as my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QMAKESPEC&lt;/code&gt; but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Qt\4.8.7\mkspecs\features\win32\windows.prf&lt;/code&gt; does not add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QT_NEEDS_QMAIN&lt;/code&gt; for this makespec, but does so for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win32-g++&lt;/code&gt;. Adding the below line to windows.prf and regenerating the Makefiles using qmake fixed the problem -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;win32-g++-4.6:DEFINES += QT_NEEDS_QMAIN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now back to the longer story of how I found the problem.&lt;/p&gt;

&lt;p&gt;I knew that Qt uses the Win32 API &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputDebugString()&lt;/code&gt; for qDebug output for GUI applications. Looking at the code for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qt_message_output()&lt;/code&gt; (the eventual function called by qDebug), I found it strange that the API was used only for WIN_CE. For WIN32 it uses fprintf()&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    if (handler) {
        (*handler)(msgType, buf);
    } else {
#if defined(Q_CC_MWERKS) &amp;amp;&amp;amp; defined(Q_OS_MACX)
        mac_default_handler(buf);
#elif defined(QT_USE_SLOG2)
        slog2_default_handler(msgType, buf);
#elif defined(Q_OS_WINCE)
        QString fstr = QString::fromLatin1(buf);
        fstr += QLatin1Char(&apos;\n&apos;);
        OutputDebugString(reinterpret_cast&amp;lt;const wchar_t&amp;gt; (fstr.utf16()));
#elif defined(Q_OS_SYMBIAN)
        // RDebug::Print has a cap of 256 characters so break it up
        char format[] = &quot;[Qt Message] %S&quot;;
        const int maxBlockSize = 256 - sizeof(format);
        const TPtrC8 ptr(reinterpret_cast&amp;lt;const tuint8&amp;gt;(buf));
        for (int i = 0; i  (fstr.utf16()));
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As an experiment, I installed a custom message handler -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void msgOut(QtMsgType /*type*/, const char* msg)
{
    QString fstr = QString::fromLatin1(msg);
    fstr += QLatin1Char(&apos;\n&apos;);
    OutputDebugString(reinterpret_cast&amp;lt;const wchar_t&amp;gt; (fstr.utf16()));
}

int main(int argc, char* argv[])
{
    ...
    qInstallMsgHandler(msgOut);
    ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That worked. But I still believed that it should work out of the box without having to install a custom handler. When looking at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qInstallMessageHandler()&lt;/code&gt; code, I noticed this interesting check -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#if defined(Q_OS_WIN) &amp;amp;&amp;amp; defined(QT_BUILD_CORE_LIB)
    if (!handler &amp;amp;&amp;amp; usingWinMain)
        handler = qWinMsgHandler;
#endif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hmmm, so when you uninstall a custom handler, instead of falling back to using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qt_message_output()&lt;/code&gt;, Qt installed a special handler &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qWinMsgHandler()&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;So, as an experiment, I installed qWinMsgHandler instead of my custom handler -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;qInstallMsgHandler(qWinMsgHandler);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That worked. Which meant, if I cleared the handler instead of setting one, it should install qWinMsgHandler internally -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;qInstallMsgHandler(0);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No dice. Huh? What? Why?&lt;/p&gt;

&lt;p&gt;Tracing that function using gdb revealed that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usingWinMain&lt;/code&gt; variable was false, so qWinMsgHandler doesn’t get installed.&lt;/p&gt;

&lt;p&gt;So where is usingWinMain set to true?&lt;/p&gt;

&lt;p&gt;In qWinMain (corelib/kernel/qcoreapplication_win.cpp).&lt;/p&gt;

&lt;p&gt;Now, that’s qWinMain, not WinMain - the usual entry point for Windows GUI applications. A helpful comment on top of that function pointed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WinMain()&lt;/code&gt; to be in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qtmain_win.cpp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And it’s there. The WinMain function and a call to qWinMain.&lt;/p&gt;

&lt;p&gt;So, why isn’t usingWinMain set to true?&lt;/p&gt;

&lt;p&gt;I asked gdb where is WinMain in the exe -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(gdb) info func WinMain
All functions matching regular expression &quot;WinMain&quot;:

File kernel\qcoreapplication_win.cpp:
void qWinMain(HINSTANCE__*, HINSTANCE__*, char*, int, int&amp;amp;, QVector&amp;lt;char&amp;gt;&amp;amp;);

Non-debugging symbols:
0x004014c0  WinMainCRTStartup
(gdb)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s strange - there is no WinMain?!&lt;/p&gt;

&lt;p&gt;Let’s look at the backtrace in gdb -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(gdb) set backtrace past-main on
(gdb) bt
#0  main (argc=1, argv=0x1f3e50) at main.cpp:64
#1  0x004013de in __tmainCRTStartup ()
#2  0x76fe8744 in KERNEL32!BaseThreadInitThunk ()
   from C:\WINDOWS\SysWOW64\kernel32.dll
#3  0x7743582d in ntdll!RtlGetAppContainerNamedObjectPath ()
   from C:\WINDOWS\SYSTEM32\ntdll.dll
#4  0x774357fd in ntdll!RtlGetAppContainerNamedObjectPath ()
   from C:\WINDOWS\SYSTEM32\ntdll.dll
#5  0x00000000 in ?? ()
(gdb)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No WinMain again. Googling around and reading some helpful stuff about &lt;a href=&quot;https://stackoverflow.com/questions/13871617/winmain-and-main-in-c-extended/&quot;&gt;main, wmain and WinMain&lt;/a&gt; and &lt;a href=&quot;http://blog.debao.me/2013/07/link-confilict-between-sdl-and-qt-under-windows/&quot;&gt;WinMain provided by Qt&lt;/a&gt;, it appears that &lt;strong&gt;For MinGW, WinMain() will never be called if main() exists&lt;/strong&gt; (if you find “official” documentation on this, please send me a pointer)!&lt;/p&gt;

&lt;p&gt;Qt works around this problem by doing this piece of preprocessing trickery in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gui\kernel\qwindowdefs.h&lt;/code&gt; -&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#if defined(QT_NEEDS_QMAIN)
#define main qMain
#endif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When QT_NEEDS_QMAIN is defined, the user provided main function is renamed to qMain forcing MinGW to use WinMain as the entry point instead of main.&lt;/p&gt;

&lt;p&gt;Does the qmake generated Makefile include QT_NEEDS_QMAIN?&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DEFINES       = -DUNICODE -DQT_DLL -DQT_SCRIPT_LIB -DQT_XML_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_HAVE_MMX -DQT_HAVE_3DNOW -DQT_HAVE_SSE -DQT_HAVE_MMXEXT -DQT_HAVE_SSE2 -DQT_THREAD_SUPPORT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;Did the older (Qt 4.3.3) qmake generated Makefile include QT_NEEDS_QMAIN?&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DEFINES       = -DUNICODE -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_SCRIPT_LIB -DQT_XML_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_THREAD_SUPPORT -DQT_NEEDS_QMAIN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;A quick experiment to add QT_NEEDS_QMAIN manually to the Makefile and re-building confirmed that it fixes the problem.&lt;/p&gt;

&lt;p&gt;Buy hey wait, we’re not done yet. I’m not manually going to add this everytime I create a new git clone or do a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make distclean&lt;/code&gt;. Why the hell isn’t qmake adding it to the generated Makefile?&lt;/p&gt;

&lt;p&gt;Now reading through C++ code to find out what is happening is one thing. Reading through qmake’s C++ code to parse a .pro file and generate a Makefile ain’t so straightforward that looking at the code will directly tell you anything without spending significant time trying to understand that code -&lt;/p&gt;

&lt;p&gt;So, change tactics and use grep inside Qt sources to find all instances of QT_NEEDS_QMAIN&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkspecs/features/win32/windows.prf:    win32-g++:DEFINES += QT_NEEDS_QMAIN
mkspecs/features/win32/windows.prf:    win32-borland:DEFINES += QT_NEEDS_QMAIN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Looking at windows.prf, it appears that if QMAKESPEC is win32-g++ or win32-borland, qmake will add QT_NEEDS_QMAIN to the generated Makefile. What Qt installer setup the makespec for me was win32-g++-4.6.&lt;/p&gt;

&lt;p&gt;Adding the below line and regenerating the Makefiles using qmake did indeed add QT_NEEDS_QMAIN to the generated Makefile -&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;win32-g++-4.6:DEFINES += QT_NEEDS_QMAIN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So that missing line is the cause of our problem.&lt;/p&gt;

&lt;p&gt;Or is it?&lt;/p&gt;

&lt;p&gt;Now Qt makespec names are essentially a combination of platform and compiler separated by a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;. Assuming 4.6 was the compiler version, I wasn’t sure if the scope &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win32-g++&lt;/code&gt; should evaluate to true for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win32-g++-4.6&lt;/code&gt; also - after all win32-g++-4.6 is a specialization of win32-g++ as evidenced by the former’s qmake.conf #including the latter.&lt;/p&gt;

&lt;p&gt;Running qmake -d and scanning the output for logs related to windows.prf processing confirmed that it does not - the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win32-g++&lt;/code&gt; scope match fails when QMAKESPEC is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;win32-g++-4.6&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I’m not sure if that scope match failure is intended and correct or a qmake bug. I spent some time trying to read through qmake source code to answer that question but gave up after about an hour - it was beyond 2:00 AM and I had already spent all evening and all night on this investigation.&lt;/p&gt;

&lt;p&gt;Aside: Although Ostinato is my personal project unrelated to my day job, I write several of these kind of “investigation reports” at the day job when debugging problems. I do it primarily to document the analysis for future use by myself or my colleagues. Reading through such a report offers the impression that the investigation was a straightforward logical line where one step leads to another till we find root cause, but this is &lt;em&gt;never ever&lt;/em&gt; the case. There are so many twists, turns, tangents and threads of investigation that you follow during the investigation that lead nowhere and are wasted effort. But this is never reflected in the report.&lt;/p&gt;

&lt;p&gt;As a matter of fact, I have long felt that “debugging” or “triaging” a technical problem is very similar to “crime” investigation - well, at least as depicted in books, tv shows and movies! That’s another post though!&lt;/p&gt;
</description>
          <pubDate>Sat, 02 Sep 2017 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/ostinato/no-qdebug-output-qt487-mingw/</link>
          <guid isPermaLink="true">https://srivatsp.com/ostinato/no-qdebug-output-qt487-mingw/</guid>
        </item>
    
  
    
	<item>
          <title>Join Runner&apos;s High you do ...</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;&lt;img src=&quot;https://srivatsp.com/assets/images/rh-yoda.png&quot; alt=&quot;Runner&apos;s High Yoda&quot; /&gt;&lt;/p&gt;
</description>
          <pubDate>Tue, 24 Jan 2017 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/yoda-rh/</link>
          <guid isPermaLink="true">https://srivatsp.com/yoda-rh/</guid>
        </item>
    
  
    
	<item>
          <title>Hello World!</title>
          <author>me@srivatsp.com (Srivats P)</author>
          <description>&lt;p&gt;There have been times when I’ve wanted to write something but didn’t ‘coz I didn’t have a “place” (aka blog) to write at. I registered this vanity domain almost an year ago with this express purpose but never managed to get it up and running for various reasons - topic for another post maybe!&lt;/p&gt;

&lt;p&gt;This whole writing business is essentially an experiment on my part. I don’t have a fixed format or theme or topic or anything decided at all. It’s just a place for when the muse strikes. I do expect to write  about &lt;a href=&quot;https://ostinato.org&quot;&gt;Ostinato&lt;/a&gt; since that is such a big part of my life, but hopefully there’ll be more. And in the spirit of the experiment that it is, the writing will likely be all over the place as I embark on this ride.&lt;/p&gt;

&lt;p&gt;Hello World!&lt;/p&gt;
</description>
          <pubDate>Wed, 04 Jan 2017 00:00:00 +0000</pubDate>
          <link>https://srivatsp.com/hello-world/</link>
          <guid isPermaLink="true">https://srivatsp.com/hello-world/</guid>
        </item>
    
  
  </channel>
</rss>
