Repository: conan-io/conan-center-index

Issue:
[request] <libnl>/<3.2.25>
### Package Details
  * Package Name/Version: **libnl/3.2.25**
  * Website: **https://www.infradead.org/~tgr/libnl**
  * Source code: **https://github.com/tgraf/libnl**


### Description Of The Library / Tool
The libnl suite is a collection of libraries providing APIs to netlink protocol based Linux kernel interfaces.
Netlink is a IPC mechanism primarly between the kernel and user space processes. It was designed to be a more flexible successor to ioctl to provide mainly networking related kernel configuration and monitoring interfaces. 

Note that this is a Linux only library.
This library is used amongst others by libpcap (on Linux), which I hope to add later.


Discussion:

Resolving patch:
diff --git a/recipes/libnl/all/conanfile.py b/recipes/libnl/all/conanfile.py
new file mode 100644
--- /dev/null
+++ b/recipes/libnl/all/conanfile.py
@@ -0,0 +1,72 @@
+import os, glob
+from conans import ConanFile, tools, AutoToolsBuildEnvironment
+from conans.errors import ConanInvalidConfiguration
+
+class LibNlConan(ConanFile):
+    name = "libnl"
+    description = "A collection of libraries providing APIs to netlink protocol based Linux kernel interfaces."
+    topics = "conan", "netlink"
+    url = "https://github.com/conan-io/conan-center-index"
+    homepage = "https://www.infradead.org/~tgr/libnl/"
+    license = "LGPL-2.1-only"
+    settings = "os", "arch", "compiler", "build_type"
+    options = {"fPIC": [True, False], "shared": [True, False]}
+    default_options = {"fPIC": True, "shared": False}
+    build_requires = ( "flex/2.6.4", "bison/3.5.3" )
+
+    _autotools = None
+
+    @property
+    def _source_subfolder(self):
+        return "source_subfolder"
+
+    def source(self):
+        tools.get(**self.conan_data["sources"][self.version])
+        extracted_dir = self.name + "-" + self.version
+        os.rename(extracted_dir, self._source_subfolder)
+
+    def configure(self):
+        if self.settings.os != "Linux":
+            raise ConanInvalidConfiguration("Libnl is only supported on Linux")
+        if self.options.shared:
+            del self.options.fPIC
+        del self.settings.compiler.libcxx
+        del self.settings.compiler.cppstd
+
+    def _configure_autotools(self):
+        if self._autotools:
+            return self._autotools
+        self._autotools = AutoToolsBuildEnvironment(self)
+        config_args = [
+            "--prefix={}".format(tools.unix_path(self.package_folder)),
+        ]
+        if self.options.shared:
+            config_args.extend(["--enable-shared=yes", "--enable-static=no"])
+        else:
+            config_args.extend(["--enable-shared=no", "--enable-static=yes"])
+
+        self._autotools.configure(configure_dir=self._source_subfolder, args=config_args)
+        return self._autotools
+
+    def build(self):
+        autotools = self._configure_autotools()
+        autotools.make()
+
+    def package(self):
+        autotools = self._configure_autotools()
+        autotools.install()
+        self.copy("COPYING", dst="licenses", src=self._source_subfolder)
+        #tools.remove_files_by_mask causes AttributeError: module 'conans.tools' has no attribute 'remove_files_by_mask'
+        #tools.remove_files_by_mask(os.path.join(self.package_folder, "lib"), "*.la")
+        la_pattern = os.path.join(self.package_folder, "lib", "**", "*.la")
+        la_files = glob.glob(la_pattern, recursive=True)
+        for next_file in la_files:
+            os.remove(next_file)
+        tools.rmdir(os.path.join(self.package_folder, "share"))
+        tools.rmdir(os.path.join(self.package_folder, "etc"))
+        tools.rmdir(os.path.join(self.package_folder, "lib", "pkgconfig"))
+
+    def package_info(self):
+        self.cpp_info.includedirs = [os.path.join('include', 'libnl3')]
+        self.cpp_info.libs = ["nl-3", "nl-cli-3", "nl-genl-3", "nl-idiag-3", "nl-nf-3", "nl-route-3"]
+        self.cpp_info.system_libs = ["pthread", "m"]


Test patch:
diff --git a/recipes/libnl/all/test_package/CMakeLists.txt b/recipes/libnl/all/test_package/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/recipes/libnl/all/test_package/CMakeLists.txt
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.5)
+project(netlink_example)
+
+include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+conan_basic_setup()
+
+add_executable(show_links show_links.c)
+target_link_libraries(show_links ${CONAN_LIBS})
diff --git a/recipes/libnl/all/test_package/conanfile.py b/recipes/libnl/all/test_package/conanfile.py
new file mode 100644
--- /dev/null
+++ b/recipes/libnl/all/test_package/conanfile.py
@@ -0,0 +1,18 @@
+import os.path
+
+from conans import ConanFile, CMake, tools
+
+
+class NetlinkTestConan(ConanFile):
+    settings = "os", "compiler", "build_type", "arch"
+    generators = "cmake"
+
+    def build(self):
+        cmake = CMake(self)
+        cmake.configure()
+        cmake.build()
+
+    def test(self):
+        if not tools.cross_building(self):
+            bin_path = os.path.join(self.build_folder, "bin", "show_links")
+            self.run(bin_path, cwd=self.source_folder, run_environment=True)
diff --git a/recipes/libnl/all/test_package/show_links.c b/recipes/libnl/all/test_package/show_links.c
new file mode 100644
--- /dev/null
+++ b/recipes/libnl/all/test_package/show_links.c
@@ -0,0 +1,164 @@
+/* Libnl example taken from stackoverflow:
+ * https://stackoverflow.com/questions/42307658/how-to-get-ipv4-address-of-an-interface-using-libnl3-netlink-version-3-on-linu
+*/
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/addr.h>
+#include <netlink/cache.h>
+#include <netlink/route/addr.h>
+
+#include <errno.h>
+
+
+
+/*
+gcc ipchange.c -o ipchange $(pkg-config --cflags --libs libnl-3.0 libnl-route-3.0 libnl-cli-3.0)
+*/
+
+#include <stdbool.h>
+
+#define ADDR_STR_BUF_SIZE 80
+
+void addr_cb(struct nl_object *p_nl_object, void *data) {
+
+    int ifindex = (int) (intptr_t) data;  // this is the link index passed as a parm
+    struct rtnl_addr *p_rtnl_addr;
+    p_rtnl_addr = (struct rtnl_addr *) p_nl_object;
+    int result;
+
+    if (NULL == p_rtnl_addr) {
+        /* error */
+        printf("addr is NULL %d\n", errno);
+        return;
+    }
+
+    // This routine is not mentioned in the doxygen help.
+    // It is listed under Attributes, but no descriptive text.
+    // this routine just returns p_rtnl_addr->a_ifindex
+    int cur_ifindex = rtnl_addr_get_ifindex(p_rtnl_addr);
+    if(cur_ifindex != ifindex) {
+        // skip interaces where the index differs.
+        return;
+    }
+
+    // Adding this to see if I can filter on ipv4 addr
+    // this routine just returns p_rtnl_addr->a_family
+    // this is not the one to use
+    // ./linux/netfilter.h:    NFPROTO_IPV6   = 10,
+    // ./linux/netfilter.h:    NFPROTO_IPV4   =  2,
+    // this is the one to use
+    // x86_64-linux-gnu/bits/socket.h
+    // defines AF_INET6 = PF_INET6 = 10
+    // defines AF_INET  = PF_INET  = 2
+    result = rtnl_addr_get_family(p_rtnl_addr);
+    // printf( "family is %d\n",result);
+    if (AF_INET6 == result) {
+    // early exit, I don't care about IPV6
+    return;
+    }
+
+    // This routine just returns p_rtnl_addr->a_local
+    struct nl_addr *p_nl_addr_local = rtnl_addr_get_local(p_rtnl_addr);
+    if (NULL == p_nl_addr_local) {
+        /* error */
+        printf("rtnl_addr_get failed\n");
+        return;
+    }
+
+    char addr_str[ADDR_STR_BUF_SIZE];
+    const char *addr_s = nl_addr2str(p_nl_addr_local, addr_str, sizeof(addr_str));
+    if (NULL == addr_s) {
+        /* error */
+        printf("nl_addr2str failed\n");
+        return;
+    }
+    fprintf(stdout, "\naddr is: %s\n", addr_s);
+
+}
+
+int main(int argc, char **argv, char **envp) {
+
+    int err, i;
+
+    struct nl_sock *p_nl_sock;
+    struct nl_cache *link_cache;
+    struct nl_cache *addr_cache;
+
+    struct rtnl_addr *p_rtnl_addr;
+    struct nl_addr *p_nl_addr;
+    struct nl_link *p_nl_link;
+    struct rtnl_link *p_rtnl_link;
+
+    char addr_str[ADDR_STR_BUF_SIZE];
+
+    p_nl_sock = nl_socket_alloc();
+    if (!p_nl_sock)
+    {
+        fprintf(stderr, "Could not allocate netlink socket.\n");
+        exit(ENOMEM);
+    }
+
+    // Connect to socket
+    if(err = nl_connect(p_nl_sock, NETLINK_ROUTE))
+    {
+        fprintf(stderr, "netlink error: %s\n", nl_geterror(err));
+        p_nl_sock = NULL;
+        exit(err);
+    }
+
+    // Either choice, the result below is a mac address
+    err = rtnl_link_alloc_cache(p_nl_sock, AF_UNSPEC, &link_cache);
+    //err = rtnl_link_alloc_cache(p_nl_sock, AF_INET, &link_cache);
+    //err = rtnl_link_alloc_cache(p_nl_sock, IFA_LOCAL, &link_cache);
+    if (0 != err)
+    {
+        /* error */
+        printf("rtnl_link_alloc_cache failed: %s\n", nl_geterror(err));
+        return(EXIT_FAILURE);
+    }
+
+    err = rtnl_addr_alloc_cache(p_nl_sock, &addr_cache);
+    if (0 != err)
+    {
+        /* error */
+        printf("rtnl_addr_alloc_cache failed: %s\n", nl_geterror(err));
+        return(EXIT_FAILURE);
+    }
+
+    // This will iterate through the cache of ip's
+    printf("Getting the list of interfaces by ip addr cache\n");
+    int count = nl_cache_nitems(addr_cache);
+    printf("addr_cache has %d items\n",count);
+    struct nl_object *p_nl_object;
+    p_nl_object = nl_cache_get_first(addr_cache);
+    p_rtnl_addr = (struct rtnl_addr *) p_nl_object;
+    for (i=0; i<count; i++)
+    {
+        // This routine just returns p_rtnl_addr->a_local
+        struct nl_addr *p_nl_addr_local = rtnl_addr_get_local(p_rtnl_addr);
+        if (NULL == p_nl_addr_local) {
+            /* error */
+            printf("rtnl_addr_get failed\n");
+            return(EXIT_FAILURE);
+        }
+
+        int cur_ifindex = rtnl_addr_get_ifindex(p_rtnl_addr);
+        printf("This is index %d\n",cur_ifindex);
+
+        const char *addr_s = nl_addr2str(p_nl_addr_local, addr_str, sizeof(addr_str));
+        if (NULL == addr_s)
+        {
+            /* error */
+            printf("nl_addr2str failed\n");
+            return(EXIT_FAILURE);
+        }
+        fprintf(stdout, "\naddr is: %s\n", addr_s);
+
+        printf("%d\n",i);
+        p_nl_object = nl_cache_get_next(p_nl_object);
+        p_rtnl_addr = (struct rtnl_addr *) p_nl_object;
+    }
+
+    return(EXIT_SUCCESS);
+}