]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
tree: Add 2 new public functions to lookup existing controllers
authorMartin Belanger <martin.belanger@dell.com>
Tue, 12 Sep 2023 19:55:55 +0000 (15:55 -0400)
committerDaniel Wagner <wagi@monom.org>
Wed, 13 Sep 2023 19:31:52 +0000 (21:31 +0200)
There is duplicate code between libnvme and nvme-cli. When trying
to instantiate a controller, both libnvme and nvme-cli scan the
sysfs looking for an existing controller that can be reused.

This patch adds nvme_ctrl_find() and nvme_ctrl_config_match().
These functions can be used by nvme-cli to lookup existing
controllers. This will allow removing the duplicate code in
nvme-cli (separate commit to follow on the nvme-cli GitHub repo).

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
src/libnvme.map
src/nvme/fabrics.c
src/nvme/private.h
src/nvme/tree.c
src/nvme/tree.h
test/tree.c

index 1765bc65c8d223984481556548af75a46e10dd73..2f12ccefb59a0136d687a140efc67643238bcaf6 100644 (file)
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 LIBNVME_1_6 {
        global:
+               nvme_ctrl_config_match;
+               nvme_ctrl_find;
                nvme_ctrl_get_src_addr;
                nvme_ctrl_release_fd;
                nvme_host_release_fds;
index 224a5c897faa2606e50bee9419b9693dcf3898a3..21fb29200b4b4bfa6ddea2f715fa5b759a0ca8a5 100644 (file)
@@ -834,6 +834,7 @@ static const char *lookup_context(nvme_root_t r, nvme_ctrl_t c)
                                               NULL,
                                               NULL,
                                               nvme_ctrl_get_trsvcid(c),
+                                              NULL,
                                               NULL))
                                return nvme_subsystem_get_application(s);
                }
@@ -863,6 +864,7 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c,
                                        nvme_ctrl_get_host_traddr(c),
                                        nvme_ctrl_get_host_iface(c),
                                        nvme_ctrl_get_trsvcid(c),
+                                       NULL,
                                        NULL);
                if (fc) {
                        const char *key;
index edb6c4e297e4f68f8f12d3ae6bbb0ea27ba9ed2e..6fb9784a696ded02a1e3e8d7101c0dd91e782879 100644 (file)
@@ -180,7 +180,7 @@ int json_dump_tree(nvme_root_t r);
 nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
                               const char *traddr, const char *host_traddr,
                               const char *host_iface, const char *trsvcid,
-                              nvme_ctrl_t p);
+                              const char *subsysnqn, nvme_ctrl_t p);
 
 #if (LOG_FUNCNAME == 1)
 #define __nvme_log_func __func__
index 989fb056401a8a543a40425194dc63e814bce5d3..07b2eefba75d394451baad166ff0a9aa52f2161a 100644 (file)
@@ -44,6 +44,8 @@
  * @host_traddr:       Host transport address (source address)
  * @host_iface:                Host interface for connection (tcp only)
  * @iface_list:                Interface list (tcp only)
+ * @addreq:            Address comparison function (for traddr, host-traddr)
+ * @well_known_nqn:    Set to "true" when @subsysnqn is the well-known NQN
  */
 struct candidate_args {
        const char *transport;
@@ -53,7 +55,10 @@ struct candidate_args {
        const char *host_traddr;
        const char *host_iface;
        struct ifaddrs *iface_list;
+       bool (*addreq)(const char *, const char *);
+       bool well_known_nqn;
 };
+typedef bool (*ctrl_match_t)(struct nvme_ctrl *c, struct candidate_args *candidate);
 
 const char *nvme_slots_sysfs_dir = "/sys/bus/pci/slots";
 
@@ -1257,7 +1262,7 @@ struct nvme_ctrl *nvme_create_ctrl(nvme_root_t r,
 static bool _tcp_ctrl_match_host_traddr_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate)
 {
        if (c->cfg.host_traddr)
-               return nvme_ipaddrs_eq(candidate->host_traddr, c->cfg.host_traddr);
+               return candidate->addreq(candidate->host_traddr, c->cfg.host_traddr);
 
        /* If c->cfg.host_traddr is NULL, then the controller (c)
         * uses the interface's primary address as the source
@@ -1405,7 +1410,7 @@ static bool _tcp_opt_params_match(struct nvme_ctrl *c, struct candidate_args *ca
 
        /* Check host_traddr only if candidate is interested */
        if (candidate->host_traddr &&
-           !nvme_ipaddrs_eq(candidate->host_traddr, src_addr))
+           !candidate->addreq(candidate->host_traddr, src_addr))
                return false;
 
        /* Check host_iface only if candidate is interested */
@@ -1418,19 +1423,12 @@ static bool _tcp_opt_params_match(struct nvme_ctrl *c, struct candidate_args *ca
 }
 
 /**
- * _tcp_lookup_ctrl() - Look for an existing controller that can be reused
- * @s: Subsystem object under which to do the search
- * @transport: Transport type ("tcp")
- * @traddr:    Destination IP address
- * @host_iface:        Interface for the connection (optional)
- * @host_traddr:       Source IP address (optional)
- * @trsvcid:   Destination TCP port
- * @p: Starting point is the linked-list (NULL to start at the beginning)
+ * _tcp_match_ctrl() - Check if controller matches candidate (TCP only)
+ * @c: An existing controller instance
+ * @candidate: Candidate ctrl we're trying to match with @c.
  *
  * We want to determine if an existing controller can be re-used
- * for the candidate controller we're trying to instantiate. The
- * candidate is identified by @transport, @traddr, @trsvcid,
- * @host_traddr, and @host_iface.
+ * for the candidate controller we're trying to instantiate.
  *
  * For TCP, we do not have a match if the candidate's transport, traddr,
  * trsvcid are not identical to those of the the existing controller.
@@ -1440,92 +1438,204 @@ static bool _tcp_opt_params_match(struct nvme_ctrl *c, struct candidate_args *ca
  * not specify them (both NULL), we can ignore them. Otherwise, we must
  * employ advanced investigation techniques to determine if there's a match.
  *
- * Return: Pointer to the matching controller or NULL.
+ * Return: true if a match is found, false otherwise.
  */
-static nvme_ctrl_t _tcp_lookup_ctrl(nvme_subsystem_t s, const char *transport,
-                                   const char *traddr, const char *host_traddr,
-                                   const char *host_iface, const char *trsvcid,
-                                   nvme_ctrl_t p)
+static bool _tcp_match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate)
 {
-       struct candidate_args candidate = {0};
-       struct nvme_ctrl *c, *matching_c = NULL;
+       if (!streq0(c->transport, candidate->transport))
+               return false;
 
-       candidate.traddr = traddr;
-       candidate.trsvcid = trsvcid;
-       candidate.transport = transport;
-       candidate.host_iface = host_iface;
-       candidate.host_traddr = host_traddr;
+       if (!streq0(c->trsvcid, candidate->trsvcid))
+               return false;
 
-       /* For TCP we may need to access the interface map. Let's retrieve
-        * and cache the map and use it for the duration of the loop below.
-        */
-       if (getifaddrs(&candidate.iface_list) == -1)
-               candidate.iface_list = NULL;
+       if (!candidate->addreq(c->traddr, candidate->traddr))
+               return false;
 
-       c = p ? nvme_subsystem_next_ctrl(s, p) : nvme_subsystem_first_ctrl(s);
-       for (; c != NULL; c = nvme_subsystem_next_ctrl(s, c)) {
-               if (!streq0(c->transport, candidate.transport))
-                       continue;
+       if (candidate->well_known_nqn && !nvme_ctrl_is_discovery_ctrl(c))
+               return false;
 
-               if (!streq0(c->trsvcid, candidate.trsvcid))
-                       continue;
+       if (candidate->subsysnqn && !streq0(c->subsysnqn, candidate->subsysnqn))
+               return false;
 
-               if (!nvme_ipaddrs_eq(c->traddr, candidate.traddr))
-                       continue;
+       /* Check host_traddr / host_iface only if candidate is interested */
+       if ((candidate->host_iface || candidate->host_traddr) &&
+           !_tcp_opt_params_match(c, candidate))
+               return false;
 
-               /* Check host_traddr / host_iface only if candidate is interested */
-               if ((candidate.host_iface || candidate.host_traddr) &&
-                   !_tcp_opt_params_match(c, &candidate))
-                       continue;
+       return true;
+}
 
-               matching_c = c;
-               break;
+/**
+ * _match_ctrl() - Check if controller matches candidate (non TCP transport)
+ * @c: An existing controller instance
+ * @candidate: Candidate ctrl we're trying to match with @c.
+ *
+ * We want to determine if an existing controller can be re-used
+ * for the candidate controller we're trying to instantiate. This function
+ * is used for all transports except TCP.
+ *
+ * Return: true if a match is found, false otherwise.
+ */
+static bool _match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate)
+{
+       if (!streq0(c->transport, candidate->transport))
+               return false;
+
+       if (candidate->traddr && c->traddr &&
+           !candidate->addreq(c->traddr, candidate->traddr))
+               return false;
+
+       if (candidate->host_traddr && c->cfg.host_traddr &&
+           !candidate->addreq(c->cfg.host_traddr, candidate->host_traddr))
+               return false;
+
+       if (candidate->host_iface && c->cfg.host_iface &&
+           !streq0(c->cfg.host_iface, candidate->host_iface))
+               return false;
+
+       if (candidate->trsvcid && c->trsvcid &&
+           !streq0(c->trsvcid, candidate->trsvcid))
+               return false;
+
+       if (candidate->well_known_nqn && !nvme_ctrl_is_discovery_ctrl(c))
+               return false;
+
+       if (candidate->subsysnqn && !streq0(c->subsysnqn, candidate->subsysnqn))
+               return false;
+
+       return true;
+}
+/**
+ * _candidate_init() - Init candidate and get the matching function
+ *
+ * @candidate:         Candidate struct to initialize
+ * @transport:         Transport name
+ * @traddr:            Transport address
+ * @trsvcid:           Transport service identifier
+ * @subsysnqn:         Subsystem NQN
+ * @host_traddr:       Host transport address
+ * @host_iface:                Host interface name
+ * @host_iface:                Host interface name
+ *
+ * The function _candidate_free() must be called to release resources once
+ * the candidate object is not longer required.
+ *
+ * Return: The matching function to use when comparing an existing
+ * controller to the candidate controller.
+ */
+static ctrl_match_t _candidate_init(struct candidate_args *candidate,
+                                   const char *transport,
+                                   const char *traddr,
+                                   const char *trsvcid,
+                                   const char *subsysnqn,
+                                   const char *host_traddr,
+                                   const char *host_iface)
+{
+       memset(candidate, 0, sizeof(*candidate));
+
+       candidate->traddr = traddr;
+       candidate->trsvcid = trsvcid;
+       candidate->transport = transport;
+       candidate->subsysnqn = subsysnqn;
+       candidate->host_iface = host_iface;
+       candidate->host_traddr = host_traddr;
+
+       if (streq0(subsysnqn, NVME_DISC_SUBSYS_NAME)) {
+               /* Since TP8013, the NQN of discovery controllers can be the
+                * well-known NQN (i.e. nqn.2014-08.org.nvmexpress.discovery) or
+                * a unique NQN. A DC created using the well-known NQN may later
+                * display a unique NQN when looked up in the sysfs. Therefore,
+                * ignore (i.e. set to NULL) the well-known NQN when looking for
+                * a match.
+                */
+               candidate->subsysnqn = NULL;
+               candidate->well_known_nqn = true;
        }
 
-       freeifaddrs(candidate.iface_list); /* This is NULL-safe */
+       if (streq0(transport, "tcp")) {
+               /* For TCP we may need to access the interface map.
+                * Let's retrieve and cache the map.
+                */
+               if (getifaddrs(&candidate->iface_list) == -1)
+                       candidate->iface_list = NULL;
 
-       return matching_c;
+               candidate->addreq = nvme_ipaddrs_eq;
+               return _tcp_match_ctrl;
+       }
+
+       if (streq0(transport, "rdma")) {
+               candidate->addreq = nvme_ipaddrs_eq;
+               return _match_ctrl;
+       }
+
+       /* All other transport types */
+       candidate->addreq = streqcase0;
+       return _match_ctrl;
+}
+
+/**
+ * _candidate_free() - Release resources allocated by _candidate_init()
+ *
+ * @candidate: data to free.
+ */
+static void _candidate_free(struct candidate_args *candidate)
+{
+       freeifaddrs(candidate->iface_list); /* This is NULL-safe */
 }
 
 nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
                               const char *traddr, const char *host_traddr,
                               const char *host_iface, const char *trsvcid,
-                              nvme_ctrl_t p)
-
+                              const char *subsysnqn, nvme_ctrl_t p)
 {
-       struct nvme_ctrl *c;
-       bool (*addreq)(const char *, const char *);
-
-       /* TCP requires special handling */
-       if (streq0(transport, "tcp"))
-               return _tcp_lookup_ctrl(s, transport, traddr,
-                                       host_traddr, host_iface, trsvcid, p);
+       struct nvme_ctrl *c, *matching_c = NULL;
+       struct candidate_args candidate;
+       ctrl_match_t ctrl_match;
 
-       if (streq0(transport, "rdma"))
-               addreq = nvme_ipaddrs_eq; /* IP address compare for RDMA */
-       else
-               addreq = streqcase0; /* Case-insensitive for everything else */
+       /* Init candidate and get the matching function to use */
+       ctrl_match = _candidate_init(&candidate, transport, traddr, trsvcid,
+                                    subsysnqn, host_traddr, host_iface);
 
        c = p ? nvme_subsystem_next_ctrl(s, p) : nvme_subsystem_first_ctrl(s);
        for (; c != NULL; c = nvme_subsystem_next_ctrl(s, c)) {
-               if (!streq0(c->transport, transport))
-                       continue;
-               if (traddr && c->traddr &&
-                   !addreq(c->traddr, traddr))
-                       continue;
-               if (host_traddr && c->cfg.host_traddr &&
-                   !addreq(c->cfg.host_traddr, host_traddr))
-                       continue;
-               if (host_iface && c->cfg.host_iface &&
-                   !streq0(c->cfg.host_iface, host_iface))
-                       continue;
-               if (trsvcid && c->trsvcid &&
-                   !streq0(c->trsvcid, trsvcid))
-                       continue;
-               return c;
+               if (ctrl_match(c, &candidate)) {
+                       matching_c = c;
+                       break;
+               }
        }
 
-       return NULL;
+       _candidate_free(&candidate);
+
+       return matching_c;
+}
+
+bool nvme_ctrl_config_match(struct nvme_ctrl *c, const char *transport,
+                           const char *traddr, const char *trsvcid,
+                           const char *subsysnqn, const char *host_traddr,
+                           const char *host_iface)
+{
+       bool match;
+       ctrl_match_t ctrl_match;
+       struct candidate_args candidate;
+
+       /* Init candidate and get the matching function to use */
+       ctrl_match = _candidate_init(&candidate, transport, traddr, trsvcid,
+                                    subsysnqn, host_traddr, host_iface);
+
+       match = ctrl_match(c, &candidate);
+
+       _candidate_free(&candidate);
+
+       return match;
+}
+
+nvme_ctrl_t nvme_ctrl_find(nvme_subsystem_t s, const char *transport,
+                          const char *traddr, const char *trsvcid,
+                          const char *subsysnqn, const char *host_traddr,
+                          const char *host_iface)
+{
+       return __nvme_lookup_ctrl(s, transport, traddr, host_traddr, host_iface,
+                                 trsvcid, subsysnqn, NULL/*p*/);
 }
 
 nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
@@ -1540,7 +1650,7 @@ nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
                return NULL;
 
        c = __nvme_lookup_ctrl(s, transport, traddr, host_traddr,
-                              host_iface, trsvcid, p);
+                              host_iface, trsvcid, NULL, p);
        if (c)
                return c;
 
index e29e6a0f11221591e418f27494d78e18829bbfc1..a30e8eb75dd6138321a816cdfff90910ca3fdfd8 100644 (file)
@@ -307,6 +307,51 @@ nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
                             const char *host_iface, const char *trsvcid,
                             nvme_ctrl_t p);
 
+/**
+ * nvme_ctrl_find() - Locate an existing controller
+ * @s:                 &nvme_subsystem_t object
+ * @transport:         Transport name
+ * @traddr:            Transport address
+ * @trsvcid:           Transport service identifier
+ * @subsysnqn:         Subsystem NQN
+ * @host_traddr:       Host transport address
+ * @host_iface:                Host interface name
+ *
+ * Lookup a controller in @s based on @transport, @traddr, @trsvcid,
+ * @subsysnqn, @host_traddr, and @host_iface. @transport must be specified,
+ * other fields may be required depending on the transport. Parameters set
+ * to NULL will be ignored.
+ *
+ * Unlike nvme_lookup_ctrl(), this function does not create a new object if
+ * an existing controller cannot be found.
+ *
+ * Return: Controller instance on success, NULL otherwise.
+ */
+nvme_ctrl_t nvme_ctrl_find(nvme_subsystem_t s, const char *transport,
+                          const char *traddr, const char *trsvcid,
+                          const char *subsysnqn, const char *host_traddr,
+                          const char *host_iface);
+
+/**
+ * nvme_ctrl_config_match() - Check if ctrl @c matches config params
+ * @c:                 An existing controller instance
+ * @transport:         Transport name
+ * @traddr:            Transport address
+ * @trsvcid:           Transport service identifier
+ * @subsysnqn:         Subsystem NQN
+ * @host_traddr:       Host transport address
+ * @host_iface:                Host interface name
+ *
+ * Check that controller @c matches parameters: @transport, @traddr,
+ * @trsvcid, @subsysnqn, @host_traddr, and @host_iface. Parameters set
+ * to NULL will be ignored.
+ *
+ * Return: true if there's a match, false otherwise.
+ */
+bool nvme_ctrl_config_match(struct nvme_ctrl *c, const char *transport,
+                           const char *traddr, const char *trsvcid,
+                           const char *subsysnqn, const char *host_traddr,
+                           const char *host_iface);
 
 /**
  * nvme_create_ctrl() - Allocate an unconnected NVMe controller
index 068ae865c0e2869829be698d0dc0ee644bf5306b..c9370f9713a5f77860428838c3ac1f518467e816 100644 (file)
@@ -419,6 +419,7 @@ struct ctrl_args {
        const char *host_traddr;
        const char *host_iface;
        const char *address;
+       const char *subsysnqn;
 };
 
 static void set_ctrl_args(struct ctrl_args *args,
@@ -427,7 +428,8 @@ static void set_ctrl_args(struct ctrl_args *args,
                          const char *trsvcid,
                          const char *host_traddr,
                          const char *host_iface,
-                         const char *address)
+                         const char *address,
+                         const char *subsysnqn)
 {
        args->transport   = transport;
        args->traddr      = traddr;
@@ -435,6 +437,7 @@ static void set_ctrl_args(struct ctrl_args *args,
        args->host_traddr = host_traddr;
        args->host_iface  = host_iface;
        args->address     = address;
+       args->subsysnqn   = subsysnqn;
 }
 
 static bool ctrl_match(const char *tag,
@@ -448,17 +451,16 @@ static bool ctrl_match(const char *tag,
        nvme_host_t h;
        nvme_ctrl_t reference_ctrl; /* Existing controller (from sysfs) */
        nvme_ctrl_t candidate_ctrl;
+       nvme_ctrl_t found_ctrl;
        nvme_subsystem_t s;
 
-       r = nvme_create_root(stdout, LOG_DEBUG);
+       r = nvme_create_root(stdout, LOG_INFO);
        assert(r);
 
-       nvme_init_logging(r, LOG_INFO, false, false);
-
        h = nvme_default_host(r);
        assert(h);
 
-       s = nvme_lookup_subsystem(h, DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN);
+       s = nvme_lookup_subsystem(h, DEFAULT_SUBSYSNAME, reference->subsysnqn ? reference->subsysnqn : DEFAULT_SUBSYSNQN);
        assert(s);
 
        reference_ctrl = nvme_lookup_ctrl(s, reference->transport, reference->traddr,
@@ -470,30 +472,52 @@ static bool ctrl_match(const char *tag,
                reference_ctrl->address = (char *)reference->address;
        }
 
+       /* nvme_ctrl_find() MUST BE RUN BEFORE nvme_lookup_ctrl() */
+       found_ctrl = nvme_ctrl_find(s, candidate->transport, candidate->traddr,
+                                   candidate->trsvcid, candidate->subsysnqn,
+                                   candidate->host_traddr,
+                                   candidate->host_iface);
+
        candidate_ctrl = nvme_lookup_ctrl(s, candidate->transport, candidate->traddr,
                                          candidate->host_traddr, candidate->host_iface,
                                          candidate->trsvcid, NULL);
 
        if (should_match) {
                if (candidate_ctrl != reference_ctrl) {
-                       printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s) failed to match (%s, %s, %s, %s, %s, %s)\n",
+                       printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s, %s) failed to match (%s, %s, %s, %s, %s, %s, %s)\n",
                               tag, reference_id, candidate_id,
                               candidate->transport, candidate->traddr, candidate->trsvcid,
-                              candidate->host_traddr, candidate->host_iface,
-                              reference->transport, reference->traddr, reference->trsvcid,
+                              candidate->subsysnqn, candidate->host_traddr, candidate->host_iface,
+                              reference->transport, reference->traddr, reference->trsvcid, reference->subsysnqn,
                               reference->host_traddr, reference->host_iface, reference->address);
                        return false;
                }
+
+               if (!found_ctrl) {
+                       printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s, %s) failed to find controller\n",
+                              tag, reference_id, candidate_id,
+                              candidate->transport, candidate->traddr, candidate->trsvcid,
+                              candidate->subsysnqn, candidate->host_traddr, candidate->host_iface);
+                       return false;
+               }
        } else {
                if (candidate_ctrl == reference_ctrl) {
-                       printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s) should not match (%s, %s, %s, %s, %s, %s)\n",
+                       printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s, %s) should not match (%s, %s, %s, %s, %s, %s, %s)\n",
                               tag, reference_id, candidate_id,
                               candidate->transport, candidate->traddr, candidate->trsvcid,
-                              candidate->host_traddr, candidate->host_iface,
-                              reference->transport, reference->traddr, reference->trsvcid,
+                              candidate->subsysnqn, candidate->host_traddr, candidate->host_iface,
+                              reference->transport, reference->traddr, reference->trsvcid, reference->subsysnqn,
                               reference->host_traddr, reference->host_iface, reference->address);
                        return false;
                }
+
+               if (found_ctrl) {
+                       printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s, %s) should not have found controller. found_ctrl=%p reference=%p\n",
+                              tag, reference_id, candidate_id,
+                              candidate->transport, candidate->traddr, candidate->trsvcid, candidate->subsysnqn,
+                              candidate->host_traddr, candidate->host_iface, found_ctrl, reference_ctrl);
+                       return false;
+               }
        }
 
        /* Set the faked data back to NULL before freeing the tree */
@@ -520,73 +544,73 @@ static bool test_ctrl_match_fc(void)
 
        /*******************************************************************/
        /* Reference ID 1 */
-       set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL);
+       set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL, NULL);
        pass &= ctrl_match("FC", 1, 0, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("FC", 1, 1, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("FC", 1, 2, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("FC", 1, 3, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("FC", 1, 4, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:21", NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:21", NULL, NULL, NULL);
        pass &= ctrl_match("FC", 1, 5, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL, NULL);
        pass &= ctrl_match("FC", 1, 6, &reference, &candidate, false);
 
 
        /*******************************************************************/
        /* Reference ID 2 */
-       set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL);
+       set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL, NULL);
        pass &= ctrl_match("FC", 2, 0, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("FC", 2, 1, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("FC", 2, 2, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("FC", 2, 3, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("FC", 2, 4, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL, NULL);
        pass &= ctrl_match("FC", 2, 5, &reference, &candidate, false);
 
 
        /*******************************************************************/
        /* Reference ID 3 */
-       set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL, NULL);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL, NULL);
        pass &= ctrl_match("FC", 3, 0, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("FC", 3, 1, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("FC", 3, 2, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("FC", 3, 3, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("FC", 3, 4, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL);
+       set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL, NULL);
        pass &= ctrl_match("FC", 3, 5, &reference, &candidate, false);
 
        return pass;
@@ -607,73 +631,73 @@ static bool test_ctrl_match_rdma(void)
 
        /*******************************************************************/
        /* Reference ID 1 */
-       set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 1, 0, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 1, 1, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("RDMA", 1, 2, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("RDMA", 1, 3, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 1, 4, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.21", NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 1, 5, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL, NULL);
        pass &= ctrl_match("RDMA", 1, 6, &reference, &candidate, false);
 
 
        /*******************************************************************/
        /* Reference ID 2 */
-       set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL);
+       set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 2, 0, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 2, 1, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("RDMA", 2, 2, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("RDMA", 2, 3, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 2, 4, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL, NULL);
        pass &= ctrl_match("RDMA", 2, 5, &reference, &candidate, false);
 
 
        /*******************************************************************/
        /* Reference ID 3 */
-       set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL, NULL);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 3, 0, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 3, 1, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("RDMA", 3, 2, &reference, &candidate, true);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("RDMA", 3, 3, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("RDMA", 3, 4, &reference, &candidate, false);
 
-       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL);
+       set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL, NULL);
        pass &= ctrl_match("RDMA", 3, 5, &reference, &candidate, false);
 
        return pass;
@@ -682,8 +706,9 @@ static bool test_ctrl_match_rdma(void)
 /**
  * test_ctrl_match_tcp - Test that we can look up TCP controllers
  *
- * @note: The mocked getifaddrs() returns 4 interface entries as follows.
- *        Therefore the tests must use IP addresses that match there.
+ * @note: The mocked getifaddrs() returns 2 interface entries with the
+ *        following addresses. Therefore the tests must use IP addresses
+ *        that match these.
  *
  *        eth0
  *         \_ 192.168.1.20
@@ -706,330 +731,442 @@ static bool test_ctrl_match_tcp()
 
        /*******************************************************************/
        /* IPv4: Reference ID 1 */
-       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 4, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 5, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 6, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 7, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 1, 8, &reference, &candidate, true);
 
        /*******************************************************************/
        /* IPv4: Reference ID 2 */
-       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 2, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv4: Reference ID 3 */
-       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 3, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv4: Reference ID 4 */
-       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL);
+       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 4, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv4: Reference ID 5 */
-       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 1, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 2, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 3, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 4, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 5, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv4: Reference ID 6 */
-       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, "traddr=123.123.123.123,trsvcid=8009,src_addr=192.168.1.20");
+       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, "traddr=123.123.123.123,trsvcid=8009,src_addr=192.168.1.20", NULL);
 
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 6, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv4: Reference ID 7 */
-       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, "traddr=123.123.123.123,trsvcid=8009,src_addr=127.0.0.1");
+       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, "traddr=123.123.123.123,trsvcid=8009,src_addr=127.0.0.1", NULL);
 
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 1, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 2, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 3, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 6, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
        pass &= ctrl_match("IPv4", 7, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv6: Reference ID 1 */
-       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 4, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 5, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 6, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 7, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 1, 8, &reference, &candidate, true);
 
        /*******************************************************************/
        /* IPv6: Reference ID 2 */
-       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL);
+       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 2, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv6: Reference ID 3 */
-       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 3, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv6: Reference ID 4 */
-       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL);
+       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 4, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv6: Reference ID 5 */
-       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL);
+       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
 
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 1, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 2, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 3, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 4, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 5, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv6: Reference ID 6 */
-       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, "traddr=aaaa::bbbb,trsvcid=8009,src_addr=fe80::dead:beef");
+       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, "traddr=aaaa::bbbb,trsvcid=8009,src_addr=fe80::dead:beef", NULL);
 
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 1, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 2, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 3, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 6, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 6, 8, &reference, &candidate, false);
 
        /*******************************************************************/
        /* IPv6: Reference ID 7 */
-       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, "traddr=aaaa::bbbb,trsvcid=8009,src_addr=::1");
+       set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, "traddr=aaaa::bbbb,trsvcid=8009,src_addr=::1", NULL);
 
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 0, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 1, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 2, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 3, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 4, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 5, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 6, &reference, &candidate, true);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 7, &reference, &candidate, false);
-       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL);
+       set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
        pass &= ctrl_match("IPv6", 7, 8, &reference, &candidate, false);
 
        return pass;
 }
 
+static bool ctrl_config_match(const char *tag,
+                             int reference_id,
+                             int candidate_id,
+                             struct ctrl_args *reference,
+                             struct ctrl_args *candidate,
+                             bool should_match)
+{
+       bool match;
+       nvme_root_t r;
+       nvme_host_t h;
+       nvme_ctrl_t reference_ctrl; /* Existing controller (from sysfs) */
+       nvme_subsystem_t s;
+
+       r = nvme_create_root(stdout, LOG_INFO);
+       assert(r);
+
+       h = nvme_default_host(r);
+       assert(h);
+
+       s = nvme_lookup_subsystem(h, DEFAULT_SUBSYSNAME, reference->subsysnqn ? reference->subsysnqn : DEFAULT_SUBSYSNQN);
+       assert(s);
+
+       reference_ctrl = nvme_lookup_ctrl(s, reference->transport, reference->traddr,
+                                         reference->host_traddr, reference->host_iface,
+                                         reference->trsvcid, NULL);
+       assert(reference_ctrl);
+       reference_ctrl->name = "nvme1";  /* fake the device name */
+       if (reference->address) {
+               reference_ctrl->address = (char *)reference->address;
+       }
+
+       match = nvme_ctrl_config_match(reference_ctrl, candidate->transport, candidate->traddr,
+                                      candidate->trsvcid, candidate->subsysnqn,
+                                      candidate->host_traddr, candidate->host_iface);
+
+       if (should_match) {
+               if (!match) {
+                       printf("%s-%d-%d: Failed to match config for Candidate (%s, %s, %s, %s, %s, %s)\n",
+                              tag, reference_id, candidate_id,
+                              candidate->transport, candidate->traddr, candidate->trsvcid,
+                              candidate->subsysnqn, candidate->host_traddr, candidate->host_iface);
+                       return false;
+               }
+       } else {
+               if (match) {
+                       printf("%s-%d-%d: Config should not have matched for Candidate (%s, %s, %s, %s, %s, %s)\n",
+                              tag, reference_id, candidate_id,
+                              candidate->transport, candidate->traddr, candidate->trsvcid,
+                              candidate->subsysnqn, candidate->host_traddr, candidate->host_iface);
+                       return false;
+               }
+       }
+
+       /* Set the faked data back to NULL before freeing the tree */
+       reference_ctrl->name = NULL;
+       reference_ctrl->address = NULL;
+
+       nvme_free_tree(r);
+
+       return true;
+}
+
+static bool test_ctrl_config_match()
+{
+       bool pass = true;
+       struct ctrl_args reference = {0};
+       struct ctrl_args candidate = {0};
+
+       printf("\n"
+              "test_ctrl_config_match:\n");
+
+       set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 0, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 1, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 2, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 3, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 4, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 5, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 6, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 7, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+       pass &= ctrl_config_match("IPv4", 1, 8, &reference, &candidate, true);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, "hello");
+       pass &= ctrl_config_match("IPv4", 1, 9, &reference, &candidate, false);
+       set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, DEFAULT_SUBSYSNQN);
+       pass &= ctrl_config_match("IPv4", 1, 9, &reference, &candidate, true);
+
+       return pass;
+}
+
 
+/**
+ * This test module uses a mocked ifaddrs library (mock-ifaddrs.c)
+ * such that there are 2 fake interfaces (eth0 and lo) with the
+ * following IP addresses:
+ *
+ *    - eth0
+ *      \_ IPv4: 192.168.1.20
+ *      \_ IPv6: fe80::dead:beef
+ *
+ *    - lo
+ *      \_ IPv4: 127.0.0.1
+ *      \_ IPv6: ::1
+ */
 int main(int argc, char *argv[])
 {
        bool pass = true;
@@ -1039,6 +1176,7 @@ int main(int argc, char *argv[])
        pass &= test_ctrl_match_fc();
        pass &= test_ctrl_match_rdma();
        pass &= test_ctrl_match_tcp();
+       pass &= test_ctrl_config_match();
 
        fflush(stdout);