The tale of Crypto++ and ns-3 CMake wrappers

Written: 2023/04/30, Rome

Tags: ns-3, CMake, external library, Crypto++, libcryptopp, undefined reference

Summary

With ns-3 switching to the CMake build system, integrating external libraries into modules or simulation scripts became slightly more complicated. In particular, the Crypto++ library seems to pose very specific issues: developers giving up on CMake support and ns-3 wrappers to CMake clashing with libcryptopp linking preferences.
This post shows an easy solution for custom ns-3 modules that require Crypto++ as a dependency.

Integrating external libraries into ns-3 modules

Starting with ns-3.36, CMake has been adopted as build system for ns-3. In order to prevent radical changes affecting users, developers included a handy wrapper that closely resembles ns-3's previous build system Waf, in terms of configuration, build, and execution options. In a similar fashion, wrappers for CMake commands have been made available for users in their CMakeLists.txt files required by ns-3 modules.

However, the previous build system Waf allowed for a rather straightforward definition of external library dependencies with respect to CMake: official documentation cites that depending on the specific third-party library, users have to carefully pick the right CMake configuration for their case [1].

The Crypto++ case

Crypto++ [2] is a free and open source library for cryptographic schemes. Understandably, there are several instances of users wanting to use said library within their simulation scripts and modules, with solutions shown for ns-3 versions as old as ns-3.22 [3, 4].

However, such builds were leveraging the previous system Waf, and current ns-3 releases apparently pose some integration problems with this library [5, 6].

Recommendations from developers

The aforementioned problems between Crypto++ and ns-3 builds based on CMake stem from developers of the former library discontinuing CMake support within the main source tree [7]. A CMake-oriented version of Crypto++ has been independently maintained by the community [8].

Assuming you are in need for an official build of Crypto++ in your ns-3.36+ module, you are most likely:

First attempt at ns-3 integration

Upon creating your ns-3 module via the integrated utility, you might be tempted to link Crypto++ as a static library following common CMake guidelines [12] and mixing them with ns-3's custom macros. The CMakeLists.txt of a module named test-cryptopp will look like this:

You can see how lines 13~15 satisfy the Crypto++ dependency via a static import, then using ns-3's custom macro build_lib, more specifically its argument LIBRARIES_TO_LINK, at line 23 we request to link libcryptopp previously added to the project.


For completeness, the following function has been defined in order to test the outcome of Crypto++ integration with ns-3:

Afterwards, you will configure and build ns-3 by launching the usual ./ns3 configure and ./ns3 build commands with your preferred options.

And the result is... Oops!

Linking errors all over the place!

Apparently this is a common error with Crypto++, and it is about the g++ -l linking option being passed early if such errors are shown while the vtables are present in the static object [13].

Diving into ns-3 CMake wrappers

Similarly to any project leveraging CMake, ns-3's main CMakeLists.txt [14] defines various project-wide options and recursively looks for sub-directories and their respective CMakeLists.txt files. The build_lib macro used in custom modules uses the LIBRARIES_TO_LINK argument in the following way [15]:

ns3-module-macros.cmake file

It is now clear how -libcryptopp would be expressed as an early option to g++, thus causing the previously seen linking errors.


Looking into other CMake files, I also found src and utils directories' CMakeLists.txt files particularly interesing [16, 17]:Β 

src CMakeLists.txt file

utils CMakeLists.txt file

Enforcing library linking order

Noticing how the LIB_AS_NEEDED_POST variable was consistently appearing near the end of the build commands, I explored the general ns-3 CMake macro file to understand what kind of values this variable was containing [18]:

macros-and-definitions.cmake file

For this reason, the LIB_AS_NEEDED_POST variable would have to also contain the -libcryptopp option, guaranteeing its correct position according to Crypto++ recommendations.

Finally getting Crypto++ to work with ns-3

Putting together all the clues, a CMakeLists.txt file for our custom module with a Crypto++ dependency as a static library would look like this:

Afterwards, you will configure and build ns-3 by launching:

export CRYPTOPP_CONFIG="-DNS3_CRYPTOPP_INCLUDE=/usr/local/include -DNS3_CRYPTOPP_LIB=/usr/local/lib/libcryptopp.a"

./ns3 configure <your-preferred-ns3-options> -- $CRYPTOPP_CONFIG

./ns3 build

And the result is...

Compilation & linking done!

Now running an example that simply runs the previously defined CryptoPPTest() function using:

./ns3 run test-cryptopp-example

Example successfully run!

Using this CMakeLists.txt solution allows for the usage of customized Crypto++ installation paths via NS3_CRYPTOPP_INCLUDE and NS3_CRYPTOPP_LIB variables, which can be specified at ns-3 configuration time prior to building.

Disclaimer

The steps in this post have been carried out in a Docker container based on Ubuntu 18.04 LTS, using ns-3.37 and Crypto++ 8.7.0. The image used is egiona/ns3-woss:u18.04-n3.37-w1.12.4 (more details).

The solution hereby presented might not be optimal if multiple ns-3 modules link the same Crypto++ dependency: I have NOT tested that situation. From my understanding, I anticipate it would be better to use this workaround sparingly: introduce the required library in an otherwise empty custom module (e.g. ns3-cryptopp-dependency), and afterwards leverage standard ns-3 module dependency system targeting the first module. This means that a new module requiring the same dependendy would list ${libns3-cryptopp-dependency} among its LIBRARIES_TO_LINK argument list. Again, I have NOT tested this case either.

CMake might also allow more elegant solutions that are unknown to me, and I encourage anyone with such experience to point it out!