summaryrefslogtreecommitdiffstats
path: root/source4/torture/nbt
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source4/torture/nbt/dgram.c699
-rw-r--r--source4/torture/nbt/nbt.c69
-rw-r--r--source4/torture/nbt/query.c115
-rw-r--r--source4/torture/nbt/register.c176
-rw-r--r--source4/torture/nbt/wins.c545
-rw-r--r--source4/torture/nbt/winsbench.c300
-rw-r--r--source4/torture/nbt/winsreplication.c9884
7 files changed, 11788 insertions, 0 deletions
diff --git a/source4/torture/nbt/dgram.c b/source4/torture/nbt/dgram.c
new file mode 100644
index 0000000..2f7ea19
--- /dev/null
+++ b/source4/torture/nbt/dgram.c
@@ -0,0 +1,699 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT dgram testing
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "torture/rpc/torture_rpc.h"
+#include "torture/nbt/proto.h"
+#include "libcli/resolve/resolve.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+#define TEST_NAME "TORTURE_TEST"
+
+/*
+ reply handler for netlogon request
+*/
+static void netlogon_handler(struct dgram_mailslot_handler *dgmslot,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *src)
+{
+ NTSTATUS status;
+ struct nbt_netlogon_response *netlogon = dgmslot->private_data;
+
+ dgmslot->private_data = netlogon = talloc(dgmslot, struct nbt_netlogon_response);
+
+ if (!dgmslot->private_data) {
+ return;
+ }
+
+ printf("netlogon reply from %s:%d\n", src->addr, src->port);
+
+ /* Fills in the netlogon pointer */
+ status = dgram_mailslot_netlogon_parse_response(netlogon, packet,
+ netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to parse netlogon packet from %s:%d\n",
+ src->addr, src->port);
+ return;
+ }
+
+}
+
+
+/* test UDP/138 netlogon requests */
+static bool nbt_test_netlogon(struct torture_context *tctx)
+{
+ struct dgram_mailslot_handler *dgmslot;
+ struct nbt_dgram_socket *dgmsock = nbt_dgram_socket_init(tctx, tctx->ev);
+ struct socket_address *dest;
+ const char *myaddress;
+ struct nbt_netlogon_packet logon;
+ struct nbt_netlogon_response *response;
+ struct nbt_name myname;
+ NTSTATUS status;
+ struct timeval tv = timeval_current();
+
+ struct socket_address *socket_address;
+
+ const char *address;
+ struct nbt_name name;
+
+ struct interface *ifaces;
+
+ name.name = lpcfg_workgroup(tctx->lp_ctx);
+ name.type = NBT_NAME_LOGON;
+ name.scope = NULL;
+
+ /* do an initial name resolution to find its IP */
+ torture_assert_ntstatus_ok(tctx,
+ resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx),
+ 0, 0,
+ &name, tctx, &address, tctx->ev),
+ talloc_asprintf(tctx, "Failed to resolve %s", name.name));
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+ myaddress = talloc_strdup(dgmsock, iface_list_best_ip(ifaces, address));
+
+
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, lpcfg_dgram_port(tctx->lp_ctx));
+ torture_assert(tctx, socket_address != NULL, "Error getting address");
+
+ /* try receiving replies on port 138 first, which will only
+ work if we are root and smbd/nmbd are not running - fall
+ back to listening on any port, which means replies from
+ most windows versions won't be seen */
+ status = socket_listen(dgmsock->sock, socket_address, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(socket_address);
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, 0);
+ torture_assert(tctx, socket_address != NULL, "Error getting address");
+
+ socket_listen(dgmsock->sock, socket_address, 0, 0);
+ }
+
+ /* setup a temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_PRIMARY_QUERY;
+ logon.req.pdc.computer_name = TEST_NAME;
+ logon.req.pdc.mailslot_name = dgmslot->mailslot_name;
+ logon.req.pdc.unicode_name = TEST_NAME;
+ logon.req.pdc.nt_version = 1;
+ logon.req.pdc.lmnt_token = 0xFFFF;
+ logon.req.pdc.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+ torture_assert(tctx, dest != NULL, "Error getting address");
+
+ status = dgram_mailslot_netlogon_send(dgmsock, &name, dest,
+ NBT_MAILSLOT_NETLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request");
+
+ while (timeval_elapsed(&tv) < 5 && !dgmslot->private_data) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert(tctx, response->response_type == NETLOGON_GET_PDC, "Got incorrect type of netlogon response");
+ torture_assert(tctx, response->data.get_pdc.command == NETLOGON_RESPONSE_FROM_PDC, "Got incorrect netlogon response command");
+
+ return true;
+}
+
+
+/* test UDP/138 netlogon requests */
+static bool nbt_test_netlogon2(struct torture_context *tctx)
+{
+ struct dgram_mailslot_handler *dgmslot;
+ struct nbt_dgram_socket *dgmsock = nbt_dgram_socket_init(tctx, tctx->ev);
+ struct socket_address *dest;
+ const char *myaddress;
+ struct nbt_netlogon_packet logon;
+ struct nbt_netlogon_response *response;
+ struct nbt_name myname;
+ NTSTATUS status;
+ struct timeval tv = timeval_current();
+
+ struct socket_address *socket_address;
+
+ const char *address;
+ struct nbt_name name;
+
+ struct interface *ifaces;
+ struct test_join *join_ctx;
+ struct cli_credentials *machine_credentials;
+ const struct dom_sid *dom_sid;
+
+ name.name = lpcfg_workgroup(tctx->lp_ctx);
+ name.type = NBT_NAME_LOGON;
+ name.scope = NULL;
+
+ /* do an initial name resolution to find its IP */
+ torture_assert_ntstatus_ok(tctx,
+ resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx),
+ 0, 0,
+ &name, tctx, &address, tctx->ev),
+ talloc_asprintf(tctx, "Failed to resolve %s", name.name));
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+ myaddress = talloc_strdup(dgmsock, iface_list_best_ip(ifaces, address));
+
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, lpcfg_dgram_port(tctx->lp_ctx));
+ torture_assert(tctx, socket_address != NULL, "Error getting address");
+
+ /* try receiving replies on port 138 first, which will only
+ work if we are root and smbd/nmbd are not running - fall
+ back to listening on any port, which means replies from
+ some windows versions won't be seen */
+ status = socket_listen(dgmsock->sock, socket_address, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(socket_address);
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, 0);
+ torture_assert(tctx, socket_address != NULL, "Error getting address");
+
+ socket_listen(dgmsock->sock, socket_address, 0, 0);
+ }
+
+ /* setup a temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_SAM_LOGON_REQUEST;
+ logon.req.logon.request_count = 0;
+ logon.req.logon.computer_name = TEST_NAME;
+ logon.req.logon.user_name = "";
+ logon.req.logon.mailslot_name = dgmslot->mailslot_name;
+ logon.req.logon.nt_version = NETLOGON_NT_VERSION_5EX_WITH_IP|NETLOGON_NT_VERSION_5|NETLOGON_NT_VERSION_1;
+ logon.req.logon.lmnt_token = 0xFFFF;
+ logon.req.logon.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock, &name, dest,
+ NBT_MAILSLOT_NETLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request");
+
+ while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
+ map_netlogon_samlogon_response(&response->data.samlogon);
+
+ torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX, "Got incorrect netlogon response command");
+
+ torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.nt_version, NETLOGON_NT_VERSION_5EX_WITH_IP|NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_1, "Got incorrect netlogon response command");
+
+ torture_assert(tctx,
+ strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") == NULL,
+ "PDC name should not be in UNC form");
+
+ /* setup (another) temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_SAM_LOGON_REQUEST;
+ logon.req.logon.request_count = 0;
+ logon.req.logon.computer_name = TEST_NAME;
+ logon.req.logon.user_name = TEST_NAME"$";
+ logon.req.logon.mailslot_name = dgmslot->mailslot_name;
+ logon.req.logon.nt_version = 1;
+ logon.req.logon.lmnt_token = 0xFFFF;
+ logon.req.logon.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock, &name, dest,
+ NBT_MAILSLOT_NETLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request");
+
+ while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
+ map_netlogon_samlogon_response(&response->data.samlogon);
+
+ torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN, "Got incorrect netlogon response command");
+
+ torture_assert_str_equal(tctx, response->data.samlogon.data.nt5_ex.user_name, TEST_NAME"$", "Got incorrect user in netlogon response");
+
+ torture_assert(tctx,
+ strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL,
+ "PDC name should be in UNC form");
+
+ join_ctx = torture_join_domain(tctx, TEST_NAME,
+ ACB_WSTRUST, &machine_credentials);
+
+ torture_assert(tctx, join_ctx != NULL,
+ talloc_asprintf(tctx, "Failed to join domain %s as %s\n",
+ lpcfg_workgroup(tctx->lp_ctx), TEST_NAME));
+
+ dom_sid = torture_join_sid(join_ctx);
+
+ /* setup (another) temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_SAM_LOGON_REQUEST;
+ logon.req.logon.request_count = 0;
+ logon.req.logon.computer_name = TEST_NAME;
+ logon.req.logon.user_name = TEST_NAME"$";
+ logon.req.logon.mailslot_name = dgmslot->mailslot_name;
+ logon.req.logon.sid = *dom_sid;
+ logon.req.logon.nt_version = 1;
+ logon.req.logon.lmnt_token = 0xFFFF;
+ logon.req.logon.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock, &name, dest,
+ NBT_MAILSLOT_NETLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request");
+
+
+ while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
+ map_netlogon_samlogon_response(&response->data.samlogon);
+
+ torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN, "Got incorrect netlogon response command");
+
+ torture_assert(tctx,
+ strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL,
+ "PDC name should be in UNC form");
+
+ /* setup (another) temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error getting a Mailslot for GetDC reply");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_SAM_LOGON_REQUEST;
+ logon.req.logon.request_count = 0;
+ logon.req.logon.computer_name = TEST_NAME;
+ logon.req.logon.user_name = TEST_NAME"$";
+ logon.req.logon.mailslot_name = dgmslot->mailslot_name;
+ logon.req.logon.sid = *dom_sid;
+ logon.req.logon.acct_control = ACB_WSTRUST;
+ logon.req.logon.nt_version = 1;
+ logon.req.logon.lmnt_token = 0xFFFF;
+ logon.req.logon.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock, &name, dest,
+ NBT_MAILSLOT_NETLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request");
+
+
+ while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
+ map_netlogon_samlogon_response(&response->data.samlogon);
+
+ torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE, "Got incorrect netlogon response command");
+
+ torture_assert(tctx,
+ strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL,
+ "PDC name should be in UNC form");
+
+ dgmslot->private_data = NULL;
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_SAM_LOGON_REQUEST;
+ logon.req.logon.request_count = 0;
+ logon.req.logon.computer_name = TEST_NAME;
+ logon.req.logon.user_name = TEST_NAME"$";
+ logon.req.logon.mailslot_name = dgmslot->mailslot_name;
+ logon.req.logon.sid = *dom_sid;
+ logon.req.logon.acct_control = ACB_NORMAL;
+ logon.req.logon.nt_version = 1;
+ logon.req.logon.lmnt_token = 0xFFFF;
+ logon.req.logon.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock, &name, dest,
+ NBT_MAILSLOT_NETLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send netlogon request");
+
+
+ while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
+ map_netlogon_samlogon_response(&response->data.samlogon);
+
+ torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN, "Got incorrect netlogon response command");
+
+ torture_assert(tctx,
+ strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL,
+ "PDC name should be in UNC form");
+
+ torture_leave_domain(tctx, join_ctx);
+ return true;
+}
+
+
+/* test UDP/138 ntlogon requests */
+static bool nbt_test_ntlogon(struct torture_context *tctx)
+{
+ struct dgram_mailslot_handler *dgmslot;
+ struct nbt_dgram_socket *dgmsock = nbt_dgram_socket_init(tctx, tctx->ev);
+ struct socket_address *dest;
+ struct test_join *join_ctx;
+ const struct dom_sid *dom_sid;
+ struct cli_credentials *machine_credentials;
+
+ const char *myaddress;
+ struct nbt_netlogon_packet logon;
+ struct nbt_netlogon_response *response;
+ struct nbt_name myname;
+ NTSTATUS status;
+ struct timeval tv = timeval_current();
+
+ struct socket_address *socket_address;
+ const char *address;
+ struct nbt_name name;
+
+ struct interface *ifaces;
+
+ name.name = lpcfg_workgroup(tctx->lp_ctx);
+ name.type = NBT_NAME_LOGON;
+ name.scope = NULL;
+
+ /* do an initial name resolution to find its IP */
+ torture_assert_ntstatus_ok(tctx,
+ resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx),
+ 0, 0, &name, tctx, &address, tctx->ev),
+ talloc_asprintf(tctx, "Failed to resolve %s", name.name));
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+ myaddress = talloc_strdup(dgmsock, iface_list_best_ip(ifaces, address));
+
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, lpcfg_dgram_port(tctx->lp_ctx));
+ torture_assert(tctx, socket_address != NULL, "Error getting address");
+
+ /* try receiving replies on port 138 first, which will only
+ work if we are root and smbd/nmbd are not running - fall
+ back to listening on any port, which means replies from
+ most windows versions won't be seen */
+ status = socket_listen(dgmsock->sock, socket_address, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(socket_address);
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, 0);
+ torture_assert(tctx, socket_address != NULL, "Error getting address");
+
+ socket_listen(dgmsock->sock, socket_address, 0, 0);
+ }
+
+ join_ctx = torture_join_domain(tctx, TEST_NAME,
+ ACB_WSTRUST, &machine_credentials);
+
+ torture_assert(tctx, join_ctx != NULL,
+ talloc_asprintf(tctx, "Failed to join domain %s as %s\n",
+ lpcfg_workgroup(tctx->lp_ctx), TEST_NAME));
+ dom_sid = torture_join_sid(join_ctx);
+
+ /* setup a temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_SAM_LOGON_REQUEST;
+ logon.req.logon.request_count = 0;
+ logon.req.logon.computer_name = TEST_NAME;
+ logon.req.logon.user_name = TEST_NAME"$";
+ logon.req.logon.mailslot_name = dgmslot->mailslot_name;
+ logon.req.logon.acct_control = ACB_WSTRUST;
+ /* Try with a SID this time */
+ logon.req.logon.sid = *dom_sid;
+ logon.req.logon.nt_version = 1;
+ logon.req.logon.lmnt_token = 0xFFFF;
+ logon.req.logon.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock,
+ &name, dest,
+ NBT_MAILSLOT_NTLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request");
+
+ while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
+ map_netlogon_samlogon_response(&response->data.samlogon);
+
+ torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE, "Got incorrect netlogon response command");
+
+ torture_assert_str_equal(tctx, response->data.samlogon.data.nt5_ex.user_name, TEST_NAME"$", "Got incorrect user in netlogon response");
+
+ torture_assert(tctx,
+ strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL,
+ "PDC name should be in UNC form");
+
+ /* setup a temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_SAM_LOGON_REQUEST;
+ logon.req.logon.request_count = 0;
+ logon.req.logon.computer_name = TEST_NAME;
+ logon.req.logon.user_name = TEST_NAME"$";
+ logon.req.logon.mailslot_name = dgmslot->mailslot_name;
+ logon.req.logon.acct_control = ACB_WSTRUST;
+ /* Leave sid as all zero */
+ logon.req.logon.nt_version = 1;
+ logon.req.logon.lmnt_token = 0xFFFF;
+ logon.req.logon.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock,
+ &name, dest,
+ NBT_MAILSLOT_NTLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request");
+
+ while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
+ map_netlogon_samlogon_response(&response->data.samlogon);
+
+ torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE, "Got incorrect netlogon response command");
+
+ torture_assert_str_equal(tctx, response->data.samlogon.data.nt5_ex.user_name, TEST_NAME"$", "Got incorrect user in netlogon response");
+
+ torture_assert(tctx,
+ strstr(response->data.samlogon.data.nt5_ex.pdc_name, "\\\\") != NULL,
+ "PDC name should be in UNC form");
+
+ /* setup (another) temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_PRIMARY_QUERY;
+ logon.req.pdc.computer_name = TEST_NAME;
+ logon.req.pdc.mailslot_name = dgmslot->mailslot_name;
+ logon.req.pdc.unicode_name = TEST_NAME;
+ logon.req.pdc.nt_version = 1;
+ logon.req.pdc.lmnt_token = 0xFFFF;
+ logon.req.pdc.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock,
+ &name, dest,
+ NBT_MAILSLOT_NTLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request");
+
+ while (timeval_elapsed(&tv) < 5 && !dgmslot->private_data) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_GET_PDC, "Got incorrect type of ntlogon response");
+ torture_assert_int_equal(tctx, response->data.get_pdc.command, NETLOGON_RESPONSE_FROM_PDC, "Got incorrect ntlogon response command");
+
+ torture_leave_domain(tctx, join_ctx);
+
+ /* setup (another) temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+ torture_assert(tctx, dgmslot != NULL, "Error temporary mailslot for GetDC");
+
+ ZERO_STRUCT(logon);
+ logon.command = LOGON_PRIMARY_QUERY;
+ logon.req.pdc.computer_name = TEST_NAME;
+ logon.req.pdc.mailslot_name = dgmslot->mailslot_name;
+ logon.req.pdc.unicode_name = TEST_NAME;
+ logon.req.pdc.nt_version = 1;
+ logon.req.pdc.lmnt_token = 0xFFFF;
+ logon.req.pdc.lm20_token = 0xFFFF;
+
+ make_nbt_name_client(&myname, TEST_NAME);
+
+ dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ address, lpcfg_dgram_port(tctx->lp_ctx));
+ torture_assert(tctx, dest != NULL, "Error getting address");
+ status = dgram_mailslot_netlogon_send(dgmsock,
+ &name, dest,
+ NBT_MAILSLOT_NTLOGON,
+ &myname, &logon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request");
+
+ while (timeval_elapsed(&tv) < 5 && !dgmslot->private_data) {
+ tevent_loop_once(dgmsock->event_ctx);
+ }
+
+ response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);
+
+ torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");
+
+ torture_assert_int_equal(tctx, response->response_type, NETLOGON_GET_PDC, "Got incorrect type of ntlogon response");
+ torture_assert_int_equal(tctx, response->data.get_pdc.command, NETLOGON_RESPONSE_FROM_PDC, "Got incorrect ntlogon response command");
+
+
+ return true;
+}
+
+
+/*
+ test nbt dgram operations
+*/
+struct torture_suite *torture_nbt_dgram(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "dgram");
+
+ torture_suite_add_simple_test(suite, "netlogon", nbt_test_netlogon);
+ torture_suite_add_simple_test(suite, "netlogon2", nbt_test_netlogon2);
+ torture_suite_add_simple_test(suite, "ntlogon", nbt_test_ntlogon);
+
+ return suite;
+}
diff --git a/source4/torture/nbt/nbt.c b/source4/torture/nbt/nbt.c
new file mode 100644
index 0000000..f350885
--- /dev/null
+++ b/source4/torture/nbt/nbt.c
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Jelmer Vernooij 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../libcli/nbt/libnbt.h"
+#include "torture/torture.h"
+#include "torture/nbt/proto.h"
+#include "torture/smbtorture.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+struct nbt_name_socket *torture_init_nbt_socket(struct torture_context *tctx)
+{
+ return nbt_name_socket_init(tctx, tctx->ev);
+}
+
+bool torture_nbt_get_name(struct torture_context *tctx,
+ struct nbt_name *name,
+ const char **address)
+{
+ make_nbt_name_server(name, strupper_talloc(tctx,
+ torture_setting_string(tctx, "host", NULL)));
+
+ /* do an initial name resolution to find its IP */
+ torture_assert_ntstatus_ok(tctx,
+ resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx),
+ 0, 0,
+ name, tctx, address, tctx->ev),
+ talloc_asprintf(tctx,
+ "Failed to resolve %s", name->name));
+
+ return true;
+}
+
+NTSTATUS torture_nbt_init(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(
+ ctx, "nbt");
+ /* nbt tests */
+ torture_suite_add_suite(suite, torture_nbt_register(suite));
+ torture_suite_add_suite(suite, torture_nbt_wins(suite));
+ torture_suite_add_suite(suite, torture_nbt_dgram(suite));
+ torture_suite_add_suite(suite, torture_nbt_winsreplication(suite));
+ torture_suite_add_suite(suite, torture_bench_nbt(suite));
+ torture_suite_add_suite(suite, torture_bench_wins(suite));
+
+ suite->description = talloc_strdup(suite,
+ "NetBIOS over TCP/IP and WINS tests");
+
+ torture_register_suite(ctx, suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/nbt/query.c b/source4/torture/nbt/query.c
new file mode 100644
index 0000000..001ff19
--- /dev/null
+++ b/source4/torture/nbt/query.c
@@ -0,0 +1,115 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT name query testing
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/torture.h"
+#include "torture/nbt/proto.h"
+#include "param/param.h"
+
+struct result_struct {
+ int num_pass;
+ int num_fail;
+};
+
+static void increment_handler(struct nbt_name_request *req)
+{
+ struct result_struct *v = talloc_get_type(req->async.private_data, struct result_struct);
+ if (req->state != NBT_REQUEST_DONE) {
+ v->num_fail++;
+ } else {
+ v->num_pass++;
+ }
+ talloc_free(req);
+}
+
+/*
+ benchmark simple name queries
+*/
+static bool bench_namequery(struct torture_context *tctx)
+{
+ struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx);
+ int num_sent=0;
+ struct result_struct *result;
+ struct nbt_name_query io;
+ struct timeval tv = timeval_current();
+ int timelimit = torture_setting_int(tctx, "timelimit", 5);
+
+ const char *address;
+ struct nbt_name name;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ io.in.name = name;
+ io.in.dest_addr = address;
+ io.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ io.in.broadcast = false;
+ io.in.wins_lookup = false;
+ io.in.timeout = 1;
+
+ result = talloc_zero(tctx, struct result_struct);
+
+ torture_comment(tctx, "Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ while (num_sent - (result->num_pass+result->num_fail) < 10) {
+ struct nbt_name_request *req;
+ req = nbt_name_query_send(nbtsock, &io);
+ torture_assert(tctx, req != NULL, "Failed to setup request!");
+ req->async.fn = increment_handler;
+ req->async.private_data = result;
+ num_sent++;
+ if (num_sent % 1000 == 0) {
+ if (torture_setting_bool(tctx, "progress", true)) {
+ torture_comment(tctx, "%.1f queries per second (%d failures) \r",
+ result->num_pass / timeval_elapsed(&tv),
+ result->num_fail);
+ fflush(stdout);
+ }
+ }
+ }
+
+ tevent_loop_once(nbtsock->event_ctx);
+ }
+
+ while (num_sent != (result->num_pass + result->num_fail)) {
+ tevent_loop_once(nbtsock->event_ctx);
+ }
+
+ torture_comment(tctx, "%.1f queries per second (%d failures) \n",
+ result->num_pass / timeval_elapsed(&tv),
+ result->num_fail);
+
+ return true;
+}
+
+
+/*
+ benchmark how fast a server can respond to name queries
+*/
+struct torture_suite *torture_bench_nbt(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "bench");
+ torture_suite_add_simple_test(suite, "namequery", bench_namequery);
+
+ return suite;
+}
diff --git a/source4/torture/nbt/register.c b/source4/torture/nbt/register.c
new file mode 100644
index 0000000..24ca328
--- /dev/null
+++ b/source4/torture/nbt/register.c
@@ -0,0 +1,176 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT name registration testing
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "torture/torture.h"
+#include "torture/nbt/proto.h"
+#include "param/param.h"
+
+#define CHECK_VALUE(tctx, v, correct) \
+ torture_assert_int_equal(tctx, v, correct, "Incorrect value")
+
+#define CHECK_STRING(tctx, v, correct) \
+ torture_assert_casestr_equal(tctx, v, correct, "Incorrect value")
+
+
+
+
+/*
+ test that a server responds correctly to attempted registrations of its name
+*/
+static bool nbt_register_own(struct torture_context *tctx)
+{
+ struct nbt_name_register io;
+ NTSTATUS status;
+ struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx);
+ struct socket_address *socket_address;
+ struct nbt_name name;
+ const char *address;
+ const char *myaddress;
+ struct interface *ifaces;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+
+ myaddress = iface_list_best_ip(ifaces, address);
+
+ socket_address = socket_address_from_strings(tctx, nbtsock->sock->backend_name,
+ myaddress, 0);
+ torture_assert(tctx, socket_address != NULL, "Unable to get address");
+
+ status = socket_listen(nbtsock->sock, socket_address, 0, 0);
+ torture_assert_ntstatus_ok(tctx, status,
+ "socket_listen for nbt_register_own failed");
+
+ torture_comment(tctx, "Testing name defense to name registration\n");
+
+ io.in.name = name;
+ io.in.dest_addr = address;
+ io.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ io.in.address = myaddress;
+ io.in.nb_flags = NBT_NODE_B | NBT_NM_ACTIVE;
+ io.in.register_demand = false;
+ io.in.broadcast = true;
+ io.in.multi_homed = false;
+ io.in.ttl = 1234;
+ io.in.timeout = 3;
+ io.in.retries = 0;
+
+ status = nbt_name_register(nbtsock, tctx, &io);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name register",
+ address));
+
+ CHECK_STRING(tctx, io.out.name.name, name.name);
+ CHECK_VALUE(tctx, io.out.name.type, name.type);
+ CHECK_VALUE(tctx, io.out.rcode, NBT_RCODE_ACT);
+
+ /* check a register demand */
+ io.in.address = myaddress;
+ io.in.register_demand = true;
+
+ status = nbt_name_register(nbtsock, tctx, &io);
+
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name register demand", address));
+
+ CHECK_STRING(tctx, io.out.name.name, name.name);
+ CHECK_VALUE(tctx, io.out.name.type, name.type);
+ CHECK_VALUE(tctx, io.out.rcode, NBT_RCODE_ACT);
+
+ return true;
+}
+
+
+/*
+ test that a server responds correctly to attempted name refresh requests
+*/
+static bool nbt_refresh_own(struct torture_context *tctx)
+{
+ struct nbt_name_refresh io;
+ NTSTATUS status;
+ struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx);
+ const char *myaddress;
+ struct socket_address *socket_address;
+ struct nbt_name name;
+ const char *address;
+ struct interface *ifaces;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+
+ myaddress = iface_list_best_ip(ifaces, address);
+
+ socket_address = socket_address_from_strings(tctx, nbtsock->sock->backend_name,
+ myaddress, 0);
+ torture_assert(tctx, socket_address != NULL,
+ "Can't parse socket address");
+
+ status = socket_listen(nbtsock->sock, socket_address, 0, 0);
+ torture_assert_ntstatus_ok(tctx, status,
+ "socket_listen for nbt_referesh_own failed");
+
+ torture_comment(tctx, "Testing name defense to name refresh\n");
+
+ io.in.name = name;
+ io.in.dest_addr = address;
+ io.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ io.in.address = myaddress;
+ io.in.nb_flags = NBT_NODE_B | NBT_NM_ACTIVE;
+ io.in.broadcast = false;
+ io.in.ttl = 1234;
+ io.in.timeout = 3;
+ io.in.retries = 0;
+
+ status = nbt_name_refresh(nbtsock, tctx, &io);
+
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name refresh", address));
+
+ CHECK_STRING(tctx, io.out.name.name, name.name);
+ CHECK_VALUE(tctx, io.out.name.type, name.type);
+ CHECK_VALUE(tctx, io.out.rcode, NBT_RCODE_ACT);
+
+ return true;
+}
+
+
+/*
+ test name registration to a server
+*/
+struct torture_suite *torture_nbt_register(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+
+ suite = torture_suite_create(mem_ctx, "register");
+ torture_suite_add_simple_test(suite, "register_own", nbt_register_own);
+ torture_suite_add_simple_test(suite, "refresh_own", nbt_refresh_own);
+
+ return suite;
+}
diff --git a/source4/torture/nbt/wins.c b/source4/torture/nbt/wins.c
new file mode 100644
index 0000000..8c847b5
--- /dev/null
+++ b/source4/torture/nbt/wins.c
@@ -0,0 +1,545 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT WINS server testing
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "torture/torture.h"
+#include "torture/nbt/proto.h"
+#include "param/param.h"
+
+#define CHECK_VALUE(tctx, v, correct) \
+ torture_assert_int_equal(tctx, v, correct, "Incorrect value")
+
+#define CHECK_STRING(tctx, v, correct) \
+ torture_assert_casestr_equal(tctx, v, correct, "Incorrect value")
+
+#define CHECK_NAME(tctx, _name, correct) do { \
+ CHECK_STRING(tctx, (_name).name, (correct).name); \
+ CHECK_VALUE(tctx, (uint8_t)(_name).type, (uint8_t)(correct).type); \
+ CHECK_STRING(tctx, (_name).scope, (correct).scope); \
+} while (0)
+
+
+/*
+ test operations against a WINS server
+*/
+static bool nbt_test_wins_name(struct torture_context *tctx, const char *address,
+ struct nbt_name *name, uint16_t nb_flags,
+ bool try_low_port,
+ uint8_t register_rcode)
+{
+ struct nbt_name_register_wins io;
+ struct nbt_name_register name_register;
+ struct nbt_name_query query;
+ struct nbt_name_refresh_wins refresh;
+ struct nbt_name_release release;
+ struct nbt_name_request *req;
+ NTSTATUS status;
+ struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx);
+ const char *myaddress;
+ struct socket_address *socket_address;
+ struct interface *ifaces;
+ bool low_port = try_low_port;
+ char **l;
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+
+ myaddress = talloc_strdup(tctx, iface_list_best_ip(ifaces, address));
+
+ socket_address = socket_address_from_strings(tctx,
+ nbtsock->sock->backend_name,
+ myaddress, lpcfg_nbt_port(tctx->lp_ctx));
+ torture_assert(tctx, socket_address != NULL,
+ "Error getting address");
+
+ /* we do the listen here to ensure the WINS server receives the packets from
+ the right IP */
+ status = socket_listen(nbtsock->sock, socket_address, 0, 0);
+ talloc_free(socket_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ low_port = false;
+ socket_address = socket_address_from_strings(tctx,
+ nbtsock->sock->backend_name,
+ myaddress, 0);
+ torture_assert(tctx, socket_address != NULL,
+ "Error getting address");
+
+ status = socket_listen(nbtsock->sock, socket_address, 0, 0);
+ talloc_free(socket_address);
+ torture_assert_ntstatus_ok(tctx, status,
+ "socket_listen for WINS failed");
+ }
+
+ torture_comment(tctx, "Testing name registration to WINS with name %s at %s nb_flags=0x%x\n",
+ nbt_name_string(tctx, name), myaddress, nb_flags);
+
+ torture_comment(tctx, "release the name\n");
+ release.in.name = *name;
+ release.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ release.in.dest_addr = address;
+ release.in.address = myaddress;
+ release.in.nb_flags = nb_flags;
+ release.in.broadcast = false;
+ release.in.timeout = 3;
+ release.in.retries = 0;
+
+ status = nbt_name_release(nbtsock, tctx, &release);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name query", address));
+ CHECK_VALUE(tctx, release.out.rcode, 0);
+
+ if (nb_flags & NBT_NM_GROUP) {
+ /* ignore this for group names */
+ } else if (!low_port) {
+ torture_comment(tctx, "no low port - skip: register the name with a wrong address\n");
+ } else {
+ torture_comment(tctx, "register the name with a wrong address (makes the next request slow!)\n");
+ io.in.name = *name;
+ io.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx);
+ io.in.wins_servers = const_str_list(
+ str_list_make_single(tctx, address));
+ io.in.addresses = const_str_list(
+ str_list_make_single(tctx, "127.64.64.1"));
+ io.in.nb_flags = nb_flags;
+ io.in.ttl = 300000;
+
+ status = nbt_name_register_wins(nbtsock, tctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "No response from %s for name register\n",
+ address));
+ }
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name register\n",
+ address));
+
+ CHECK_STRING(tctx, io.out.wins_server, address);
+ CHECK_VALUE(tctx, io.out.rcode, 0);
+
+ torture_comment(tctx, "register the name correct address\n");
+ name_register.in.name = *name;
+ name_register.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ name_register.in.dest_addr = address;
+ name_register.in.address = myaddress;
+ name_register.in.nb_flags = nb_flags;
+ name_register.in.register_demand= false;
+ name_register.in.broadcast = false;
+ name_register.in.multi_homed = true;
+ name_register.in.ttl = 300000;
+ name_register.in.timeout = 3;
+ name_register.in.retries = 2;
+
+ /*
+ * test if the server ignores resent requests
+ */
+ req = nbt_name_register_send(nbtsock, &name_register);
+ while (true) {
+ tevent_loop_once(nbtsock->event_ctx);
+ if (req->state != NBT_REQUEST_WAIT) {
+ break;
+ }
+ if (req->received_wack) {
+ /*
+ * if we received the wack response
+ * we resend the request and the
+ * server should ignore that
+ * and not handle it as new request
+ */
+ req->state = NBT_REQUEST_SEND;
+ DLIST_ADD_END(nbtsock->send_queue, req);
+ TEVENT_FD_WRITEABLE(nbtsock->fde);
+ break;
+ }
+ }
+
+ status = nbt_name_register_recv(req, tctx, &name_register);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "No response from %s for name register\n",
+ address));
+ }
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name register\n",
+ address));
+
+ CHECK_VALUE(tctx, name_register.out.rcode, 0);
+ CHECK_STRING(tctx, name_register.out.reply_addr, myaddress);
+ }
+
+ torture_comment(tctx, "register the name correct address\n");
+ io.in.name = *name;
+ io.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx);
+ l = str_list_make_single(tctx, address);
+ io.in.wins_servers = discard_const_p(const char *, l);
+ l = str_list_make_single(tctx, myaddress);
+ io.in.addresses = discard_const_p(const char *, l);
+ io.in.nb_flags = nb_flags;
+ io.in.ttl = 300000;
+
+ status = nbt_name_register_wins(nbtsock, tctx, &io);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register", address));
+
+ CHECK_STRING(tctx, io.out.wins_server, address);
+ CHECK_VALUE(tctx, io.out.rcode, register_rcode);
+
+ if (register_rcode != NBT_RCODE_OK) {
+ return true;
+ }
+
+ if (name->type != NBT_NAME_MASTER &&
+ name->type != NBT_NAME_LOGON &&
+ name->type != NBT_NAME_BROWSER &&
+ (nb_flags & NBT_NM_GROUP)) {
+ torture_comment(tctx, "Try to register as non-group\n");
+ io.in.nb_flags &= ~NBT_NM_GROUP;
+ status = nbt_name_register_wins(nbtsock, tctx, &io);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register\n",
+ address));
+ CHECK_VALUE(tctx, io.out.rcode, NBT_RCODE_ACT);
+ }
+
+ torture_comment(tctx, "query the name to make sure its there\n");
+ query.in.name = *name;
+ query.in.dest_addr = address;
+ query.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ query.in.broadcast = false;
+ query.in.wins_lookup = true;
+ query.in.timeout = 3;
+ query.in.retries = 0;
+
+ status = nbt_name_query(nbtsock, tctx, &query);
+ if (name->type == NBT_NAME_MASTER) {
+ torture_assert_ntstatus_equal(
+ tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ talloc_asprintf(tctx, "Bad response from %s for name query", address));
+ return true;
+ }
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name query", address));
+
+ CHECK_NAME(tctx, query.out.name, *name);
+ CHECK_VALUE(tctx, query.out.num_addrs, 1);
+ if (name->type != NBT_NAME_LOGON &&
+ (nb_flags & NBT_NM_GROUP)) {
+ CHECK_STRING(tctx, query.out.reply_addrs[0], "255.255.255.255");
+ } else {
+ CHECK_STRING(tctx, query.out.reply_addrs[0], myaddress);
+ }
+
+
+ query.in.name.name = strupper_talloc(tctx, name->name);
+ if (query.in.name.name &&
+ strcmp(query.in.name.name, name->name) != 0) {
+ torture_comment(tctx, "check case sensitivity\n");
+ status = nbt_name_query(nbtsock, tctx, &query);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, talloc_asprintf(tctx, "Bad response from %s for name query", address));
+ }
+
+ query.in.name = *name;
+ if (name->scope) {
+ query.in.name.scope = strupper_talloc(tctx, name->scope);
+ }
+ if (query.in.name.scope &&
+ strcmp(query.in.name.scope, name->scope) != 0) {
+ torture_comment(tctx, "check case sensitivity on scope\n");
+ status = nbt_name_query(nbtsock, tctx, &query);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, talloc_asprintf(tctx, "Bad response from %s for name query", address));
+ }
+
+ torture_comment(tctx, "refresh the name\n");
+ refresh.in.name = *name;
+ refresh.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx);
+ l = str_list_make_single(tctx, address);
+ refresh.in.wins_servers = discard_const_p(const char *, l);
+ l = str_list_make_single(tctx, myaddress);
+ refresh.in.addresses = discard_const_p(const char *, l);
+ refresh.in.nb_flags = nb_flags;
+ refresh.in.ttl = 12345;
+
+ status = nbt_name_refresh_wins(nbtsock, tctx, &refresh);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "No response from %s for name refresh",
+ address));
+ }
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name refresh",
+ address));
+
+ CHECK_STRING(tctx, refresh.out.wins_server, address);
+ CHECK_VALUE(tctx, refresh.out.rcode, 0);
+
+ printf("release the name\n");
+ release.in.name = *name;
+ release.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ release.in.dest_addr = address;
+ release.in.address = myaddress;
+ release.in.nb_flags = nb_flags;
+ release.in.broadcast = false;
+ release.in.timeout = 3;
+ release.in.retries = 0;
+
+ status = nbt_name_release(nbtsock, tctx, &release);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "No response from %s for name release",
+ address));
+ }
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name release",
+ address));
+
+ CHECK_NAME(tctx, release.out.name, *name);
+ CHECK_VALUE(tctx, release.out.rcode, 0);
+
+ if (nb_flags & NBT_NM_GROUP) {
+ /* ignore this for group names */
+ } else if (!low_port) {
+ torture_comment(tctx, "no low port - skip: register the name with a wrong address\n");
+ } else {
+ torture_comment(tctx, "register the name with a wrong address (makes the next request slow!)\n");
+ io.in.name = *name;
+ io.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx);
+ io.in.wins_servers = const_str_list(
+ str_list_make_single(tctx, address));
+ io.in.addresses = const_str_list(
+ str_list_make_single(tctx, "127.64.64.1"));
+ io.in.nb_flags = nb_flags;
+ io.in.ttl = 300000;
+
+ status = nbt_name_register_wins(nbtsock, tctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "No response from %s for name register\n",
+ address));
+ }
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name register\n",
+ address));
+
+ CHECK_STRING(tctx, io.out.wins_server, address);
+ CHECK_VALUE(tctx, io.out.rcode, 0);
+ }
+
+ torture_comment(tctx, "refresh the name with the correct address\n");
+ refresh.in.name = *name;
+ refresh.in.wins_port = lpcfg_nbt_port(tctx->lp_ctx);
+ refresh.in.wins_servers = const_str_list(
+ str_list_make_single(tctx, address));
+ refresh.in.addresses = const_str_list(
+ str_list_make_single(tctx, myaddress));
+ refresh.in.nb_flags = nb_flags;
+ refresh.in.ttl = 12345;
+
+ status = nbt_name_refresh_wins(nbtsock, tctx, &refresh);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "No response from %s for name refresh",
+ address));
+ }
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name refresh",
+ address));
+
+ CHECK_STRING(tctx, refresh.out.wins_server, address);
+ CHECK_VALUE(tctx, refresh.out.rcode, 0);
+
+ torture_comment(tctx, "release the name\n");
+ release.in.name = *name;
+ release.in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ release.in.dest_addr = address;
+ release.in.address = myaddress;
+ release.in.nb_flags = nb_flags;
+ release.in.broadcast = false;
+ release.in.timeout = 3;
+ release.in.retries = 0;
+
+ status = nbt_name_release(nbtsock, tctx, &release);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name query", address));
+
+ CHECK_NAME(tctx, release.out.name, *name);
+ CHECK_VALUE(tctx, release.out.rcode, 0);
+
+ torture_comment(tctx, "release again\n");
+ status = nbt_name_release(nbtsock, tctx, &release);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "Bad response from %s for name query",
+ address));
+
+ CHECK_NAME(tctx, release.out.name, *name);
+ CHECK_VALUE(tctx, release.out.rcode, 0);
+
+
+ torture_comment(tctx, "query the name to make sure its gone\n");
+ query.in.name = *name;
+ status = nbt_name_query(nbtsock, tctx, &query);
+ if (name->type != NBT_NAME_LOGON &&
+ (nb_flags & NBT_NM_GROUP)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ "ERROR: Name query failed after group release");
+ } else {
+ torture_assert_ntstatus_equal(tctx, status,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ "Incorrect response to name query");
+ }
+
+ return true;
+}
+
+
+static char *test_nbt_wins_scope_string(TALLOC_CTX *mem_ctx, uint8_t count)
+{
+ char *res;
+ uint8_t i;
+
+ res = talloc_array(mem_ctx, char, count+1);
+ if (res == NULL) {
+ return NULL;
+ }
+
+ for (i=0; i < count; i++) {
+ switch (i) {
+ case 63:
+ case 63 + 1 + 63:
+ case 63 + 1 + 63 + 1 + 63:
+ res[i] = '.';
+ break;
+ default:
+ res[i] = '0' + (i%10);
+ break;
+ }
+ }
+
+ res[count] = '\0';
+
+ talloc_set_name_const(res, res);
+
+ return res;
+}
+
+/*
+ test operations against a WINS server
+*/
+static bool nbt_test_wins(struct torture_context *tctx)
+{
+ struct nbt_name name;
+ uint32_t r = (uint32_t)(random() % (100000));
+ const char *address;
+ bool ret = true;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ name.name = talloc_asprintf(tctx, "_TORTURE-%5u", r);
+
+ name.type = NBT_NAME_CLIENT;
+ name.scope = NULL;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, true, NBT_RCODE_OK);
+
+ name.type = NBT_NAME_MASTER;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_OK);
+
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H | NBT_NM_GROUP, false, NBT_RCODE_OK);
+
+ name.type = NBT_NAME_SERVER;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, true, NBT_RCODE_OK);
+
+ name.type = NBT_NAME_LOGON;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H | NBT_NM_GROUP, false, NBT_RCODE_OK);
+
+ name.type = NBT_NAME_BROWSER;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H | NBT_NM_GROUP, false, NBT_RCODE_OK);
+
+ name.type = NBT_NAME_PDC;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, true, NBT_RCODE_OK);
+
+ name.type = 0xBF;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, true, NBT_RCODE_OK);
+
+ name.type = 0xBE;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_OK);
+
+ name.scope = "example";
+ name.type = 0x72;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, true, NBT_RCODE_OK);
+
+ name.scope = "example";
+ name.type = 0x71;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H | NBT_NM_GROUP, false, NBT_RCODE_OK);
+
+ name.scope = "foo.example.com";
+ name.type = 0x72;
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_OK);
+
+ name.name = talloc_asprintf(tctx, "_T\01-%5u.foo", r);
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_OK);
+
+ name.name = "";
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_OK);
+
+ name.name = talloc_asprintf(tctx, ".");
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_OK);
+
+ name.name = talloc_asprintf(tctx, "%5u-\377\200\300FOO", r);
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_OK);
+
+ name.scope = test_nbt_wins_scope_string(tctx, 237);
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_OK);
+
+ name.scope = test_nbt_wins_scope_string(tctx, 238);
+ ret &= nbt_test_wins_name(tctx, address, &name,
+ NBT_NODE_H, false, NBT_RCODE_SVR);
+
+ return ret;
+}
+
+/*
+ test WINS operations
+*/
+struct torture_suite *torture_nbt_wins(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "wins");
+
+ torture_suite_add_simple_test(suite, "wins", nbt_test_wins);
+
+ return suite;
+}
diff --git a/source4/torture/nbt/winsbench.c b/source4/torture/nbt/winsbench.c
new file mode 100644
index 0000000..3722202
--- /dev/null
+++ b/source4/torture/nbt/winsbench.c
@@ -0,0 +1,300 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS benchmark test
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "torture/torture.h"
+#include "torture/nbt/proto.h"
+#include "param/param.h"
+
+struct wins_state {
+ int num_names;
+ bool *registered;
+ int pass_count;
+ int fail_count;
+ const char *wins_server;
+ uint16_t wins_port;
+ const char *my_ip;
+ uint32_t ttl;
+};
+
+struct idx_state {
+ int idx;
+ struct wins_state *state;
+};
+
+static struct nbt_name generate_name(TALLOC_CTX *tctx, int idx)
+{
+ struct nbt_name name;
+ name.name = talloc_asprintf(tctx, "WINSBench%6u", idx);
+ name.type = 0x4;
+ name.scope = NULL;
+ return name;
+}
+
+static void register_handler(struct nbt_name_request *req)
+{
+ struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state);
+ struct wins_state *state = istate->state;
+ struct nbt_name_register io;
+ NTSTATUS status;
+
+ status = nbt_name_register_recv(req, istate, &io);
+ if (!NT_STATUS_IS_OK(status) || io.out.rcode != NBT_RCODE_OK) {
+ state->fail_count++;
+ } else {
+ state->pass_count++;
+ state->registered[istate->idx] = true;
+ }
+ talloc_free(istate);
+}
+
+/*
+ generate a registration
+*/
+static void generate_register(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx)
+{
+ struct nbt_name_register io;
+ TALLOC_CTX *tmp_ctx = talloc_new(state);
+ struct nbt_name_request *req;
+ struct idx_state *istate;
+
+ istate = talloc(nbtsock, struct idx_state);
+ istate->idx = idx;
+ istate->state = state;
+
+ io.in.name = generate_name(tmp_ctx, idx);
+ io.in.dest_addr = state->wins_server;
+ io.in.dest_port = state->wins_port;
+ io.in.address = state->my_ip;
+ io.in.nb_flags = NBT_NODE_H;
+ io.in.register_demand = false;
+ io.in.broadcast = false;
+ io.in.multi_homed = false;
+ io.in.ttl = state->ttl;
+ io.in.timeout = 2;
+ io.in.retries = 1;
+
+ req = nbt_name_register_send(nbtsock, &io);
+
+ req->async.fn = register_handler;
+ req->async.private_data = istate;
+
+ talloc_free(tmp_ctx);
+}
+
+
+static void release_handler(struct nbt_name_request *req)
+{
+ struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state);
+ struct wins_state *state = istate->state;
+ struct nbt_name_release io;
+ NTSTATUS status;
+
+ status = nbt_name_release_recv(req, istate, &io);
+ if (state->registered[istate->idx] &&
+ (!NT_STATUS_IS_OK(status) || io.out.rcode != NBT_RCODE_OK)) {
+ state->fail_count++;
+ } else {
+ state->pass_count++;
+ state->registered[istate->idx] = false;
+ }
+ talloc_free(istate);
+}
+
+/*
+ generate a name release
+*/
+static void generate_release(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx)
+{
+ struct nbt_name_release io;
+ TALLOC_CTX *tmp_ctx = talloc_new(state);
+ struct nbt_name_request *req;
+ struct idx_state *istate;
+
+ istate = talloc(nbtsock, struct idx_state);
+ istate->idx = idx;
+ istate->state = state;
+
+ io.in.name = generate_name(tmp_ctx, idx);
+ io.in.dest_port = state->wins_port;
+ io.in.dest_addr = state->wins_server;
+ io.in.address = state->my_ip;
+ io.in.nb_flags = NBT_NODE_H;
+ io.in.broadcast = false;
+ io.in.timeout = 2;
+ io.in.retries = 1;
+
+ req = nbt_name_release_send(nbtsock, &io);
+
+ req->async.fn = release_handler;
+ req->async.private_data = istate;
+
+ talloc_free(tmp_ctx);
+}
+
+
+static void query_handler(struct nbt_name_request *req)
+{
+ struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state);
+ struct wins_state *state = istate->state;
+ struct nbt_name_query io;
+ NTSTATUS status;
+
+ status = nbt_name_query_recv(req, istate, &io);
+ if (!NT_STATUS_IS_OK(status) && state->registered[istate->idx]) {
+ state->fail_count++;
+ } else {
+ state->pass_count++;
+ }
+ talloc_free(istate);
+}
+
+/*
+ generate a name query
+*/
+static void generate_query(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx)
+{
+ struct nbt_name_query io;
+ TALLOC_CTX *tmp_ctx = talloc_new(state);
+ struct nbt_name_request *req;
+ struct idx_state *istate;
+
+ istate = talloc(nbtsock, struct idx_state);
+ istate->idx = idx;
+ istate->state = state;
+
+ io.in.name = generate_name(tmp_ctx, idx);
+ io.in.dest_addr = state->wins_server;
+ io.in.dest_port = state->wins_port;
+ io.in.broadcast = false;
+ io.in.wins_lookup = true;
+ io.in.timeout = 2;
+ io.in.retries = 1;
+
+ req = nbt_name_query_send(nbtsock, &io);
+
+ req->async.fn = query_handler;
+ req->async.private_data = istate;
+
+ talloc_free(tmp_ctx);
+}
+
+/*
+ generate one WINS request
+*/
+static void generate_request(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx)
+{
+ if (random() % 5 == 0) {
+ generate_register(nbtsock, state, idx);
+ return;
+ }
+
+ if (random() % 20 == 0) {
+ generate_release(nbtsock, state, idx);
+ return;
+ }
+
+ generate_query(nbtsock, state, idx);
+}
+
+/*
+ benchmark simple name queries
+*/
+static bool bench_wins(struct torture_context *tctx)
+{
+ struct nbt_name_socket *nbtsock = nbt_name_socket_init(tctx, tctx->ev);
+ int num_sent=0;
+ struct timeval tv = timeval_current();
+ bool ret = true;
+ int timelimit = torture_setting_int(tctx, "timelimit", 5);
+ struct wins_state *state;
+ extern int torture_entries;
+ struct socket_address *my_ip;
+ struct nbt_name name;
+ const char *address;
+ struct interface *ifaces;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ state = talloc_zero(nbtsock, struct wins_state);
+
+ state->num_names = torture_entries;
+ state->registered = talloc_zero_array(state, bool, state->num_names);
+ state->wins_server = address;
+ state->wins_port = lpcfg_nbt_port(tctx->lp_ctx);
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+ state->my_ip = talloc_strdup(tctx, iface_list_best_ip(ifaces, address));
+ state->ttl = timelimit;
+
+ my_ip = socket_address_from_strings(nbtsock, nbtsock->sock->backend_name,
+ state->my_ip, 0);
+
+ socket_listen(nbtsock->sock, my_ip, 0, 0);
+
+ torture_comment(tctx, "Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ while (num_sent - (state->pass_count+state->fail_count) < 10) {
+ generate_request(nbtsock, state, num_sent % state->num_names);
+ num_sent++;
+ if (num_sent % 50 == 0) {
+ if (torture_setting_bool(tctx, "progress", true)) {
+ torture_comment(tctx, "%.1f queries per second (%d failures) \r",
+ state->pass_count / timeval_elapsed(&tv),
+ state->fail_count);
+ fflush(stdout);
+ }
+ }
+ }
+
+ tevent_loop_once(nbtsock->event_ctx);
+ }
+
+ while (num_sent != (state->pass_count + state->fail_count)) {
+ tevent_loop_once(nbtsock->event_ctx);
+ }
+
+ torture_comment(tctx, "%.1f queries per second (%d failures) \n",
+ state->pass_count / timeval_elapsed(&tv),
+ state->fail_count);
+
+ talloc_free(nbtsock);
+ return ret;
+}
+
+
+/*
+ benchmark how fast a WINS server can respond to a mixture of
+ registration/refresh/release and name query requests
+*/
+struct torture_suite *torture_bench_wins(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "bench-wins");
+
+ torture_suite_add_simple_test(suite, "wins", bench_wins);
+
+ return suite;
+}
diff --git a/source4/torture/nbt/winsreplication.c b/source4/torture/nbt/winsreplication.c
new file mode 100644
index 0000000..60dd4c4
--- /dev/null
+++ b/source4/torture/nbt/winsreplication.c
@@ -0,0 +1,9884 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS replication testing
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/wrepl/winsrepl.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "libcli/nbt/libnbt.h"
+#include "torture/torture.h"
+#include "torture/nbt/proto.h"
+#include "param/param.h"
+
+#define CHECK_STATUS(tctx, status, correct) \
+ torture_assert_ntstatus_equal(tctx, status, correct, \
+ "Incorrect status")
+
+#define CHECK_VALUE(tctx, v, correct) \
+ torture_assert(tctx, (v) == (correct), \
+ talloc_asprintf(tctx, "Incorrect value %s=%d - should be %d\n", \
+ #v, v, correct))
+
+#define CHECK_VALUE_UINT64(tctx, v, correct) \
+ torture_assert(tctx, (v) == (correct), \
+ talloc_asprintf(tctx, "Incorrect value %s=%llu - should be %llu\n", \
+ #v, (long long)v, (long long)correct))
+
+#define CHECK_VALUE_STRING(tctx, v, correct) \
+ torture_assert_str_equal(tctx, v, correct, "Invalid value")
+
+#define _NBT_NAME(n,t,s) {\
+ .name = n,\
+ .type = t,\
+ .scope = s\
+}
+
+static const char *wrepl_name_type_string(enum wrepl_name_type type)
+{
+ switch (type) {
+ case WREPL_TYPE_UNIQUE: return "UNIQUE";
+ case WREPL_TYPE_GROUP: return "GROUP";
+ case WREPL_TYPE_SGROUP: return "SGROUP";
+ case WREPL_TYPE_MHOMED: return "MHOMED";
+ }
+ return "UNKNOWN_TYPE";
+}
+
+static const char *wrepl_name_state_string(enum wrepl_name_state state)
+{
+ switch (state) {
+ case WREPL_STATE_ACTIVE: return "ACTIVE";
+ case WREPL_STATE_RELEASED: return "RELEASED";
+ case WREPL_STATE_TOMBSTONE: return "TOMBSTONE";
+ case WREPL_STATE_RESERVED: return "RESERVED";
+ }
+ return "UNKNOWN_STATE";
+}
+
+/*
+ test how assoc_ctx's are only usable on the connection
+ they are created on.
+*/
+static bool test_assoc_ctx1(struct torture_context *tctx)
+{
+ bool ret = true;
+ struct tevent_req *subreq;
+ struct wrepl_socket *wrepl_socket1;
+ struct wrepl_associate associate1;
+ struct wrepl_socket *wrepl_socket2;
+ struct wrepl_associate associate2;
+ struct wrepl_packet packet;
+ struct wrepl_send_ctrl ctrl;
+ struct wrepl_packet *rep_packet;
+ struct wrepl_associate_stop assoc_stop;
+ NTSTATUS status;
+ struct nbt_name name;
+ const char *address;
+ bool ok;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ torture_comment(tctx, "Test if assoc_ctx is only valid on the connection it was created on\n");
+
+ wrepl_socket1 = wrepl_socket_init(tctx, tctx->ev);
+ wrepl_socket2 = wrepl_socket_init(tctx, tctx->ev);
+
+ torture_comment(tctx, "Setup 2 wrepl connections\n");
+ status = wrepl_connect(wrepl_socket1, wrepl_best_ip(tctx->lp_ctx, address), address);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ status = wrepl_connect(wrepl_socket2, wrepl_best_ip(tctx->lp_ctx, address), address);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Send a start association request (conn1)\n");
+ status = wrepl_associate(wrepl_socket1, &associate1);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "association context (conn1): 0x%x\n", associate1.out.assoc_ctx);
+
+ torture_comment(tctx, "Send a start association request (conn2)\n");
+ status = wrepl_associate(wrepl_socket2, &associate2);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "association context (conn2): 0x%x\n", associate2.out.assoc_ctx);
+
+ torture_comment(tctx, "Send a replication table query, with assoc 1 (conn2), the answer should be on conn1\n");
+ ZERO_STRUCT(packet);
+ packet.opcode = WREPL_OPCODE_BITS;
+ packet.assoc_ctx = associate1.out.assoc_ctx;
+ packet.mess_type = WREPL_REPLICATION;
+ packet.message.replication.command = WREPL_REPL_TABLE_QUERY;
+ ZERO_STRUCT(ctrl);
+ ctrl.send_only = true;
+ subreq = wrepl_request_send(tctx, tctx->ev, wrepl_socket2, &packet, &ctrl);
+ ok = tevent_req_poll(subreq, tctx->ev);
+ if (!ok) {
+ CHECK_STATUS(tctx, NT_STATUS_INTERNAL_ERROR, NT_STATUS_OK);
+ }
+ status = wrepl_request_recv(subreq, tctx, &rep_packet);
+ TALLOC_FREE(subreq);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Send a association request (conn2), to make sure the last request was ignored\n");
+ status = wrepl_associate(wrepl_socket2, &associate2);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Send a replication table query, with invalid assoc (conn1), receive answer from conn2\n");
+ ZERO_STRUCT(packet);
+ packet.opcode = WREPL_OPCODE_BITS;
+ packet.assoc_ctx = 0;
+ packet.mess_type = WREPL_REPLICATION;
+ packet.message.replication.command = WREPL_REPL_TABLE_QUERY;
+ status = wrepl_request(wrepl_socket1, tctx, &packet, &rep_packet);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Send a association request (conn1), to make sure the last request was handled correct\n");
+ status = wrepl_associate(wrepl_socket1, &associate2);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ assoc_stop.in.assoc_ctx = associate1.out.assoc_ctx;
+ assoc_stop.in.reason = 4;
+ torture_comment(tctx, "Send a association stop request (conn1), reson: %u\n", assoc_stop.in.reason);
+ status = wrepl_associate_stop(wrepl_socket1, &assoc_stop);
+ CHECK_STATUS(tctx, status, NT_STATUS_END_OF_FILE);
+
+ assoc_stop.in.assoc_ctx = associate2.out.assoc_ctx;
+ assoc_stop.in.reason = 0;
+ torture_comment(tctx, "Send a association stop request (conn2), reson: %u\n", assoc_stop.in.reason);
+ status = wrepl_associate_stop(wrepl_socket2, &assoc_stop);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Close 2 wrepl connections\n");
+ talloc_free(wrepl_socket1);
+ talloc_free(wrepl_socket2);
+ return ret;
+}
+
+/*
+ test if we always get back the same assoc_ctx
+*/
+static bool test_assoc_ctx2(struct torture_context *tctx)
+{
+ struct wrepl_socket *wrepl_socket;
+ struct wrepl_associate associate;
+ uint32_t assoc_ctx1;
+ struct nbt_name name;
+ NTSTATUS status;
+ const char *address;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ torture_comment(tctx, "Test if we always get back the same assoc_ctx\n");
+
+ wrepl_socket = wrepl_socket_init(tctx, tctx->ev);
+
+ torture_comment(tctx, "Setup wrepl connections\n");
+ status = wrepl_connect(wrepl_socket, wrepl_best_ip(tctx->lp_ctx, address), address);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Send 1st start association request\n");
+ status = wrepl_associate(wrepl_socket, &associate);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ assoc_ctx1 = associate.out.assoc_ctx;
+ torture_comment(tctx, "1st association context: 0x%x\n", associate.out.assoc_ctx);
+
+ torture_comment(tctx, "Send 2nd start association request\n");
+ status = wrepl_associate(wrepl_socket, &associate);
+ torture_assert_ntstatus_ok(tctx, status, "2nd start association failed");
+ torture_assert(tctx, associate.out.assoc_ctx == assoc_ctx1,
+ "Different context returned");
+ torture_comment(tctx, "2nd association context: 0x%x\n", associate.out.assoc_ctx);
+
+ torture_comment(tctx, "Send 3rd start association request\n");
+ status = wrepl_associate(wrepl_socket, &associate);
+ torture_assert(tctx, associate.out.assoc_ctx == assoc_ctx1,
+ "Different context returned");
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ torture_comment(tctx, "3rd association context: 0x%x\n", associate.out.assoc_ctx);
+
+ torture_comment(tctx, "Close wrepl connections\n");
+ talloc_free(wrepl_socket);
+ return true;
+}
+
+
+/*
+ display a replication entry
+*/
+static void display_entry(struct torture_context *tctx, struct wrepl_name *name)
+{
+ int i;
+
+ torture_comment(tctx, "%s\n", nbt_name_string(tctx, &name->name));
+ torture_comment(tctx, "\tTYPE:%u STATE:%u NODE:%u STATIC:%u VERSION_ID: %llu\n",
+ name->type, name->state, name->node, name->is_static, (long long)name->version_id);
+ torture_comment(tctx, "\tRAW_FLAGS: 0x%08X OWNER: %-15s\n",
+ name->raw_flags, name->owner);
+ for (i=0;i<name->num_addresses;i++) {
+ torture_comment(tctx, "\tADDR: %-15s OWNER: %-15s\n",
+ name->addresses[i].address, name->addresses[i].owner);
+ }
+}
+
+/*
+ test a full replication dump from a WINS server
+*/
+static bool test_wins_replication(struct torture_context *tctx)
+{
+ struct wrepl_socket *wrepl_socket;
+ NTSTATUS status;
+ int i, j;
+ struct wrepl_associate associate;
+ struct wrepl_pull_table pull_table;
+ struct wrepl_pull_names pull_names;
+ struct nbt_name name;
+ const char *address;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ torture_comment(tctx, "Test one pull replication cycle\n");
+
+ wrepl_socket = wrepl_socket_init(tctx, tctx->ev);
+
+ torture_comment(tctx, "Setup wrepl connections\n");
+ status = wrepl_connect(wrepl_socket, wrepl_best_ip(tctx->lp_ctx, address), address);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Send a start association request\n");
+
+ status = wrepl_associate(wrepl_socket, &associate);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "association context: 0x%x\n", associate.out.assoc_ctx);
+
+ torture_comment(tctx, "Send a replication table query\n");
+ pull_table.in.assoc_ctx = associate.out.assoc_ctx;
+
+ status = wrepl_pull_table(wrepl_socket, tctx, &pull_table);
+ if (NT_STATUS_EQUAL(NT_STATUS_NETWORK_ACCESS_DENIED,status)) {
+ struct wrepl_associate_stop assoc_stop;
+
+ assoc_stop.in.assoc_ctx = associate.out.assoc_ctx;
+ assoc_stop.in.reason = 0;
+
+ wrepl_associate_stop(wrepl_socket, &assoc_stop);
+
+ torture_fail(tctx, "We are not a valid pull partner for the server");
+ }
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Found %d replication partners\n", pull_table.out.num_partners);
+
+ for (i=0;i<pull_table.out.num_partners;i++) {
+ struct wrepl_wins_owner *partner = &pull_table.out.partners[i];
+ torture_comment(tctx, "%s max_version=%6llu min_version=%6llu type=%d\n",
+ partner->address,
+ (long long)partner->max_version,
+ (long long)partner->min_version,
+ partner->type);
+
+ pull_names.in.assoc_ctx = associate.out.assoc_ctx;
+ pull_names.in.partner = *partner;
+
+ status = wrepl_pull_names(wrepl_socket, tctx, &pull_names);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_comment(tctx, "Received %d names\n", pull_names.out.num_names);
+
+ for (j=0;j<pull_names.out.num_names;j++) {
+ display_entry(tctx, &pull_names.out.names[j]);
+ }
+ }
+
+ torture_comment(tctx, "Close wrepl connections\n");
+ talloc_free(wrepl_socket);
+ return true;
+}
+
+struct test_wrepl_conflict_conn {
+ const char *address;
+ struct wrepl_socket *pull;
+ uint32_t pull_assoc;
+
+#define TEST_OWNER_A_ADDRESS "127.65.65.1"
+#define TEST_ADDRESS_A_PREFIX "127.0.65"
+#define TEST_OWNER_B_ADDRESS "127.66.66.1"
+#define TEST_ADDRESS_B_PREFIX "127.0.66"
+#define TEST_OWNER_X_ADDRESS "127.88.88.1"
+#define TEST_ADDRESS_X_PREFIX "127.0.88"
+
+ struct wrepl_wins_owner a, b, c, x;
+
+ struct socket_address *myaddr;
+ struct socket_address *myaddr2;
+ struct nbt_name_socket *nbtsock;
+ struct nbt_name_socket *nbtsock2;
+
+ struct nbt_name_socket *nbtsock_srv;
+ struct nbt_name_socket *nbtsock_srv2;
+
+ uint32_t addresses_best_num;
+ struct wrepl_ip *addresses_best;
+
+ uint32_t addresses_best2_num;
+ struct wrepl_ip *addresses_best2;
+
+ uint32_t addresses_all_num;
+ struct wrepl_ip *addresses_all;
+
+ uint32_t addresses_mhomed_num;
+ struct wrepl_ip *addresses_mhomed;
+};
+
+static const struct wrepl_ip addresses_A_1[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".1"
+ }
+};
+static const struct wrepl_ip addresses_A_2[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".2"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4_X_3_4[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4_B_3_4[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4_OWNER_B[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4_X_3_4_OWNER_B[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".4"
+ }
+};
+/*
+static const struct wrepl_ip addresses_A_3_4_X_1_2[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".1"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".2"
+ }
+};
+*/
+static const struct wrepl_ip addresses_B_1[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".1"
+ }
+};
+/*
+static const struct wrepl_ip addresses_B_2[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".2"
+ }
+};
+*/
+static const struct wrepl_ip addresses_B_3_4[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_B_3_4_X_3_4[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_B_3_4_X_1_2[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".1"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".2"
+ }
+};
+
+/*
+static const struct wrepl_ip addresses_X_1_2[] = {
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".1"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".2"
+ }
+};
+*/
+
+static const struct wrepl_ip addresses_X_3_4[] = {
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".4"
+ }
+};
+
+static struct test_wrepl_conflict_conn *test_create_conflict_ctx(
+ struct torture_context *tctx, const char *address)
+{
+ struct test_wrepl_conflict_conn *ctx;
+ struct wrepl_associate associate;
+ struct wrepl_pull_table pull_table;
+ struct socket_address *nbt_srv_addr;
+ NTSTATUS status;
+ uint32_t i;
+ uint32_t num_ifaces;
+ struct interface *ifaces;
+
+ ctx = talloc_zero(tctx, struct test_wrepl_conflict_conn);
+ if (!ctx) return NULL;
+
+ ctx->address = address;
+ ctx->pull = wrepl_socket_init(ctx, tctx->ev);
+ if (!ctx->pull) return NULL;
+
+ torture_comment(tctx, "Setup wrepl conflict pull connection\n");
+ status = wrepl_connect(ctx->pull, wrepl_best_ip(tctx->lp_ctx, ctx->address), ctx->address);
+ if (!NT_STATUS_IS_OK(status)) return NULL;
+
+ status = wrepl_associate(ctx->pull, &associate);
+ if (!NT_STATUS_IS_OK(status)) return NULL;
+
+ ctx->pull_assoc = associate.out.assoc_ctx;
+
+ ctx->a.address = TEST_OWNER_A_ADDRESS;
+ ctx->a.max_version = 0;
+ ctx->a.min_version = 0;
+ ctx->a.type = 1;
+
+ ctx->b.address = TEST_OWNER_B_ADDRESS;
+ ctx->b.max_version = 0;
+ ctx->b.min_version = 0;
+ ctx->b.type = 1;
+
+ ctx->x.address = TEST_OWNER_X_ADDRESS;
+ ctx->x.max_version = 0;
+ ctx->x.min_version = 0;
+ ctx->x.type = 1;
+
+ ctx->c.address = address;
+ ctx->c.max_version = 0;
+ ctx->c.min_version = 0;
+ ctx->c.type = 1;
+
+ pull_table.in.assoc_ctx = ctx->pull_assoc;
+ status = wrepl_pull_table(ctx->pull, ctx->pull, &pull_table);
+ if (!NT_STATUS_IS_OK(status)) return NULL;
+
+ for (i=0; i < pull_table.out.num_partners; i++) {
+ if (strcmp(TEST_OWNER_A_ADDRESS,pull_table.out.partners[i].address)==0) {
+ ctx->a.max_version = pull_table.out.partners[i].max_version;
+ ctx->a.min_version = pull_table.out.partners[i].min_version;
+ }
+ if (strcmp(TEST_OWNER_B_ADDRESS,pull_table.out.partners[i].address)==0) {
+ ctx->b.max_version = pull_table.out.partners[i].max_version;
+ ctx->b.min_version = pull_table.out.partners[i].min_version;
+ }
+ if (strcmp(TEST_OWNER_X_ADDRESS,pull_table.out.partners[i].address)==0) {
+ ctx->x.max_version = pull_table.out.partners[i].max_version;
+ ctx->x.min_version = pull_table.out.partners[i].min_version;
+ }
+ if (strcmp(address,pull_table.out.partners[i].address)==0) {
+ ctx->c.max_version = pull_table.out.partners[i].max_version;
+ ctx->c.min_version = pull_table.out.partners[i].min_version;
+ }
+ }
+
+ talloc_free(pull_table.out.partners);
+
+ ctx->nbtsock = nbt_name_socket_init(ctx, tctx->ev);
+ if (!ctx->nbtsock) return NULL;
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+
+ ctx->myaddr = socket_address_from_strings(tctx, ctx->nbtsock->sock->backend_name, iface_list_best_ip(ifaces, address), 0);
+ if (!ctx->myaddr) return NULL;
+
+ for (i = 0; i < iface_list_count(ifaces); i++) {
+ if (!iface_list_n_is_v4(ifaces, i)) continue;
+ if (strcmp(ctx->myaddr->addr, iface_list_n_ip(ifaces, i)) == 0) continue;
+ ctx->myaddr2 = socket_address_from_strings(tctx, ctx->nbtsock->sock->backend_name, iface_list_n_ip(ifaces, i), 0);
+ if (!ctx->myaddr2) return NULL;
+ break;
+ }
+
+ status = socket_listen(ctx->nbtsock->sock, ctx->myaddr, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) return NULL;
+
+ ctx->nbtsock_srv = nbt_name_socket_init(ctx, tctx->ev);
+ if (!ctx->nbtsock_srv) return NULL;
+
+ /* Make a port 137 version of ctx->myaddr */
+ nbt_srv_addr = socket_address_from_strings(tctx, ctx->nbtsock_srv->sock->backend_name, ctx->myaddr->addr, lpcfg_nbt_port(tctx->lp_ctx));
+ if (!nbt_srv_addr) return NULL;
+
+ /* And if possible, bind to it. This won't work unless we are root or in sockewrapper */
+ status = socket_listen(ctx->nbtsock_srv->sock, nbt_srv_addr, 0, 0);
+ talloc_free(nbt_srv_addr);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* this isn't fatal */
+ talloc_free(ctx->nbtsock_srv);
+ ctx->nbtsock_srv = NULL;
+ }
+
+ if (ctx->myaddr2 && ctx->nbtsock_srv) {
+ ctx->nbtsock2 = nbt_name_socket_init(ctx, tctx->ev);
+ if (!ctx->nbtsock2) return NULL;
+
+ status = socket_listen(ctx->nbtsock2->sock, ctx->myaddr2, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) return NULL;
+
+ ctx->nbtsock_srv2 = nbt_name_socket_init(ctx, ctx->nbtsock_srv->event_ctx);
+ if (!ctx->nbtsock_srv2) return NULL;
+
+ /* Make a port 137 version of ctx->myaddr2 */
+ nbt_srv_addr = socket_address_from_strings(tctx,
+ ctx->nbtsock_srv->sock->backend_name,
+ ctx->myaddr2->addr,
+ lpcfg_nbt_port(tctx->lp_ctx));
+ if (!nbt_srv_addr) return NULL;
+
+ /* And if possible, bind to it. This won't work unless we are root or in sockewrapper */
+ status = socket_listen(ctx->nbtsock_srv2->sock, ctx->myaddr2, 0, 0);
+ talloc_free(nbt_srv_addr);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* this isn't fatal */
+ talloc_free(ctx->nbtsock_srv2);
+ ctx->nbtsock_srv2 = NULL;
+ }
+ }
+
+ ctx->addresses_best_num = 1;
+ ctx->addresses_best = talloc_array(ctx, struct wrepl_ip, ctx->addresses_best_num);
+ if (!ctx->addresses_best) return NULL;
+ ctx->addresses_best[0].owner = ctx->b.address;
+ ctx->addresses_best[0].ip = ctx->myaddr->addr;
+
+
+ num_ifaces = iface_list_count(ifaces);
+ ctx->addresses_all = talloc_array(ctx, struct wrepl_ip, num_ifaces);
+ ctx->addresses_all_num = 0;
+ if (!ctx->addresses_all) return NULL;
+ for (i=0; i < num_ifaces; i++) {
+ if (!iface_list_n_is_v4(ifaces, i)) continue;
+ ctx->addresses_all[i].owner = ctx->b.address;
+ ctx->addresses_all[i].ip = talloc_strdup(ctx->addresses_all, iface_list_n_ip(ifaces, i));
+ ctx->addresses_all_num++;
+ if (!ctx->addresses_all[i].ip) return NULL;
+ }
+
+ if (ctx->nbtsock_srv2) {
+ ctx->addresses_best2_num = 1;
+ ctx->addresses_best2 = talloc_array(ctx, struct wrepl_ip, ctx->addresses_best2_num);
+ if (!ctx->addresses_best2) return NULL;
+ ctx->addresses_best2[0].owner = ctx->b.address;
+ ctx->addresses_best2[0].ip = ctx->myaddr2->addr;
+
+ ctx->addresses_mhomed_num = 2;
+ ctx->addresses_mhomed = talloc_array(ctx, struct wrepl_ip, ctx->addresses_mhomed_num);
+ if (!ctx->addresses_mhomed) return NULL;
+ ctx->addresses_mhomed[0].owner = ctx->b.address;
+ ctx->addresses_mhomed[0].ip = ctx->myaddr->addr;
+ ctx->addresses_mhomed[1].owner = ctx->b.address;
+ ctx->addresses_mhomed[1].ip = ctx->myaddr2->addr;
+ }
+
+ return ctx;
+}
+
+static bool test_wrepl_update_one(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx,
+ const struct wrepl_wins_owner *owner,
+ const struct wrepl_wins_name *name)
+{
+ struct wrepl_socket *wrepl_socket;
+ struct wrepl_associate associate;
+ struct wrepl_packet update_packet, repl_send;
+ struct wrepl_table *update;
+ struct wrepl_wins_owner wrepl_wins_owners[1];
+ struct wrepl_packet *repl_recv;
+ struct wrepl_send_reply *send_reply;
+ struct wrepl_wins_name wrepl_wins_names[1];
+ uint32_t assoc_ctx;
+ NTSTATUS status;
+
+ wrepl_socket = wrepl_socket_init(ctx, tctx->ev);
+
+ status = wrepl_connect(wrepl_socket, wrepl_best_ip(tctx->lp_ctx, ctx->address), ctx->address);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ status = wrepl_associate(wrepl_socket, &associate);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ assoc_ctx = associate.out.assoc_ctx;
+
+ /* now send a WREPL_REPL_UPDATE message */
+ ZERO_STRUCT(update_packet);
+ update_packet.opcode = WREPL_OPCODE_BITS;
+ update_packet.assoc_ctx = assoc_ctx;
+ update_packet.mess_type = WREPL_REPLICATION;
+ update_packet.message.replication.command = WREPL_REPL_UPDATE;
+ update = &update_packet.message.replication.info.table;
+
+ update->partner_count = ARRAY_SIZE(wrepl_wins_owners);
+ update->partners = wrepl_wins_owners;
+ update->initiator = "0.0.0.0";
+
+ wrepl_wins_owners[0] = *owner;
+
+ status = wrepl_request(wrepl_socket, wrepl_socket,
+ &update_packet, &repl_recv);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VALUE(tctx, repl_recv->mess_type, WREPL_REPLICATION);
+ CHECK_VALUE(tctx, repl_recv->message.replication.command, WREPL_REPL_SEND_REQUEST);
+
+ ZERO_STRUCT(repl_send);
+ repl_send.opcode = WREPL_OPCODE_BITS;
+ repl_send.assoc_ctx = assoc_ctx;
+ repl_send.mess_type = WREPL_REPLICATION;
+ repl_send.message.replication.command = WREPL_REPL_SEND_REPLY;
+ send_reply = &repl_send.message.replication.info.reply;
+
+ send_reply->num_names = ARRAY_SIZE(wrepl_wins_names);
+ send_reply->names = wrepl_wins_names;
+
+ wrepl_wins_names[0] = *name;
+
+ status = wrepl_request(wrepl_socket, wrepl_socket,
+ &repl_send, &repl_recv);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VALUE(tctx, repl_recv->mess_type, WREPL_STOP_ASSOCIATION);
+ CHECK_VALUE(tctx, repl_recv->message.stop.reason, 0);
+
+ talloc_free(wrepl_socket);
+ return true;
+}
+
+static bool test_wrepl_is_applied(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx,
+ const struct wrepl_wins_owner *owner,
+ const struct wrepl_wins_name *name,
+ bool expected)
+{
+ NTSTATUS status;
+ struct wrepl_pull_names pull_names;
+ struct wrepl_name *names;
+
+ pull_names.in.assoc_ctx = ctx->pull_assoc;
+ pull_names.in.partner = *owner;
+ pull_names.in.partner.min_version = pull_names.in.partner.max_version;
+
+ status = wrepl_pull_names(ctx->pull, ctx->pull, &pull_names);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ torture_assert(tctx, pull_names.out.num_names == (expected?1:0),
+ talloc_asprintf(tctx, "Invalid number of records returned - expected %d got %d", expected, pull_names.out.num_names));
+
+ names = pull_names.out.names;
+
+ if (expected) {
+ uint32_t flags = WREPL_NAME_FLAGS(names[0].type,
+ names[0].state,
+ names[0].node,
+ names[0].is_static);
+ char *expected_scope = NULL;
+ CHECK_VALUE(tctx, names[0].name.type, name->name->type);
+ CHECK_VALUE_STRING(tctx, names[0].name.name, name->name->name);
+
+ if (names[0].name.scope) {
+ expected_scope = talloc_strndup(tctx,
+ name->name->scope,
+ 237);
+ }
+ CHECK_VALUE_STRING(tctx, names[0].name.scope, expected_scope);
+ CHECK_VALUE(tctx, flags, name->flags);
+ CHECK_VALUE_UINT64(tctx, names[0].version_id, name->id);
+
+ if (flags & 2) {
+ CHECK_VALUE(tctx, names[0].num_addresses,
+ name->addresses.addresses.num_ips);
+ } else {
+ CHECK_VALUE(tctx, names[0].num_addresses, 1);
+ CHECK_VALUE_STRING(tctx, names[0].addresses[0].address,
+ name->addresses.ip);
+ }
+ }
+ talloc_free(pull_names.out.names);
+ return true;
+}
+
+static bool test_wrepl_mhomed_merged(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx,
+ const struct wrepl_wins_owner *owner1,
+ uint32_t num_ips1, const struct wrepl_ip *ips1,
+ const struct wrepl_wins_owner *owner2,
+ uint32_t num_ips2, const struct wrepl_ip *ips2,
+ const struct wrepl_wins_name *name2)
+{
+ NTSTATUS status;
+ struct wrepl_pull_names pull_names;
+ struct wrepl_name *names;
+ uint32_t flags;
+ uint32_t i, j;
+ uint32_t num_ips = num_ips1 + num_ips2;
+
+ for (i = 0; i < num_ips2; i++) {
+ for (j = 0; j < num_ips1; j++) {
+ if (strcmp(ips2[i].ip,ips1[j].ip) == 0) {
+ num_ips--;
+ break;
+ }
+ }
+ }
+
+ pull_names.in.assoc_ctx = ctx->pull_assoc;
+ pull_names.in.partner = *owner2;
+ pull_names.in.partner.min_version = pull_names.in.partner.max_version;
+
+ status = wrepl_pull_names(ctx->pull, ctx->pull, &pull_names);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VALUE(tctx, pull_names.out.num_names, 1);
+
+ names = pull_names.out.names;
+
+ flags = WREPL_NAME_FLAGS(names[0].type,
+ names[0].state,
+ names[0].node,
+ names[0].is_static);
+ CHECK_VALUE(tctx, names[0].name.type, name2->name->type);
+ CHECK_VALUE_STRING(tctx, names[0].name.name, name2->name->name);
+ CHECK_VALUE_STRING(tctx, names[0].name.scope, name2->name->scope);
+ CHECK_VALUE(tctx, flags, name2->flags | WREPL_TYPE_MHOMED);
+ CHECK_VALUE_UINT64(tctx, names[0].version_id, name2->id);
+
+ CHECK_VALUE(tctx, names[0].num_addresses, num_ips);
+
+ for (i = 0; i < names[0].num_addresses; i++) {
+ const char *addr = names[0].addresses[i].address;
+ const char *owner = names[0].addresses[i].owner;
+ bool found = false;
+
+ for (j = 0; j < num_ips2; j++) {
+ if (strcmp(addr, ips2[j].ip) == 0) {
+ found = true;
+ CHECK_VALUE_STRING(tctx, owner, owner2->address);
+ break;
+ }
+ }
+
+ if (found) continue;
+
+ for (j = 0; j < num_ips1; j++) {
+ if (strcmp(addr, ips1[j].ip) == 0) {
+ found = true;
+ CHECK_VALUE_STRING(tctx, owner, owner1->address);
+ break;
+ }
+ }
+
+ if (found) continue;
+
+ CHECK_VALUE_STRING(tctx, addr, "not found in address list");
+ }
+ talloc_free(pull_names.out.names);
+ return true;
+}
+
+static bool test_wrepl_sgroup_merged(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx,
+ struct wrepl_wins_owner *merge_owner,
+ struct wrepl_wins_owner *owner1,
+ uint32_t num_ips1, const struct wrepl_ip *ips1,
+ struct wrepl_wins_owner *owner2,
+ uint32_t num_ips2, const struct wrepl_ip *ips2,
+ const struct wrepl_wins_name *name2)
+{
+ NTSTATUS status;
+ struct wrepl_pull_names pull_names;
+ struct wrepl_name *names;
+ struct wrepl_name *name = NULL;
+ uint32_t flags;
+ uint32_t i, j;
+ uint32_t num_ips = num_ips1 + num_ips2;
+
+ if (!merge_owner) {
+ merge_owner = &ctx->c;
+ }
+
+ for (i = 0; i < num_ips1; i++) {
+ if (owner1 != &ctx->c && strcmp(ips1[i].owner,owner2->address) == 0) {
+ num_ips--;
+ continue;
+ }
+ for (j = 0; j < num_ips2; j++) {
+ if (strcmp(ips1[i].ip,ips2[j].ip) == 0) {
+ num_ips--;
+ break;
+ }
+ }
+ }
+
+
+ pull_names.in.assoc_ctx = ctx->pull_assoc;
+ pull_names.in.partner = *merge_owner;
+ pull_names.in.partner.min_version = pull_names.in.partner.max_version;
+ pull_names.in.partner.max_version = 0;
+
+ status = wrepl_pull_names(ctx->pull, ctx->pull, &pull_names);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ names = pull_names.out.names;
+
+ for (i = 0; i < pull_names.out.num_names; i++) {
+ if (names[i].name.type != name2->name->type) continue;
+ if (!names[i].name.name) continue;
+ if (strcmp(names[i].name.name, name2->name->name) != 0) continue;
+ if (names[i].name.scope) continue;
+
+ name = &names[i];
+ }
+
+ if (pull_names.out.num_names > 0) {
+ merge_owner->max_version = names[pull_names.out.num_names-1].version_id;
+ }
+
+ if (!name) {
+ torture_comment(tctx, "%s: Name '%s' not found\n", __location__, nbt_name_string(ctx, name2->name));
+ return false;
+ }
+
+ flags = WREPL_NAME_FLAGS(name->type,
+ name->state,
+ name->node,
+ name->is_static);
+ CHECK_VALUE(tctx, name->name.type, name2->name->type);
+ CHECK_VALUE_STRING(tctx, name->name.name, name2->name->name);
+ CHECK_VALUE_STRING(tctx, name->name.scope, name2->name->scope);
+ CHECK_VALUE(tctx, flags, name2->flags);
+
+ CHECK_VALUE(tctx, name->num_addresses, num_ips);
+
+ for (i = 0; i < name->num_addresses; i++) {
+ const char *addr = name->addresses[i].address;
+ const char *owner = name->addresses[i].owner;
+ bool found = false;
+
+ for (j = 0; j < num_ips2; j++) {
+ if (strcmp(addr, ips2[j].ip) == 0) {
+ found = true;
+ CHECK_VALUE_STRING(tctx, owner, ips2[j].owner);
+ break;
+ }
+ }
+
+ if (found) continue;
+
+ for (j = 0; j < num_ips1; j++) {
+ if (strcmp(addr, ips1[j].ip) == 0) {
+ found = true;
+ if (owner1 == &ctx->c) {
+ CHECK_VALUE_STRING(tctx, owner, owner1->address);
+ } else {
+ CHECK_VALUE_STRING(tctx, owner, ips1[j].owner);
+ }
+ break;
+ }
+ }
+
+ if (found) continue;
+
+ CHECK_VALUE_STRING(tctx, addr, "not found in address list");
+ }
+ talloc_free(pull_names.out.names);
+ return true;
+}
+
+static char *test_nbt_winsrepl_scope_string(TALLOC_CTX *mem_ctx, uint8_t count)
+{
+ char *res;
+ uint8_t i;
+
+ res = talloc_array(mem_ctx, char, count+1);
+ if (res == NULL) {
+ return NULL;
+ }
+
+ for (i=0; i < count; i++) {
+ res[i] = '0' + (i%10);
+ }
+
+ res[count] = '\0';
+
+ talloc_set_name_const(res, res);
+
+ return res;
+}
+
+static bool test_conflict_same_owner(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx)
+{
+ bool ret = true;
+ struct wrepl_wins_name wins_name1;
+ struct wrepl_wins_name wins_name2;
+ struct wrepl_wins_name *wins_name_tmp;
+ struct wrepl_wins_name *wins_name_last;
+ struct wrepl_wins_name *wins_name_cur;
+ uint32_t i,j;
+ struct nbt_name names[] = {
+ _NBT_NAME("_SAME_OWNER_A", 0x00, NULL),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 1)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 2)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 3)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 4)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 5)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 6)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 7)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 8)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 9)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 237)),
+ _NBT_NAME("_SAME_OWNER_A", 0x00,
+ test_nbt_winsrepl_scope_string(tctx, 238)),
+ _NBT_NAME("_SAME_OWNER_A", 0x1C, NULL),
+ };
+ struct {
+ enum wrepl_name_type type;
+ enum wrepl_name_state state;
+ enum wrepl_name_node node;
+ bool is_static;
+ uint32_t num_ips;
+ const struct wrepl_ip *ips;
+ } records[] = {
+ {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ },{
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ },{
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_2),
+ .ips = addresses_A_2,
+ },{
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = true,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ },{
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_2),
+ .ips = addresses_A_2,
+ },{
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_2),
+ .ips = addresses_A_2,
+ },{
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ },{
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_2),
+ .ips = addresses_A_2,
+ },{
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ },{
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ },{
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ },{
+ /* the last one should always be a unique,tomstone record! */
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ }
+ };
+
+ wins_name_tmp = NULL;
+ wins_name_last = &wins_name2;
+ wins_name_cur = &wins_name1;
+
+ for (j=0; ret && j < ARRAY_SIZE(names); j++) {
+ torture_comment(tctx, "Test Replica Conflicts with same owner[%s] for %s\n",
+ nbt_name_string(ctx, &names[j]), ctx->a.address);
+
+ for(i=0; ret && i < ARRAY_SIZE(records); i++) {
+ wins_name_tmp = wins_name_last;
+ wins_name_last = wins_name_cur;
+ wins_name_cur = wins_name_tmp;
+
+ if (i > 0) {
+ torture_comment(tctx, "%s,%s%s vs. %s,%s%s with %s ip(s) => %s\n",
+ wrepl_name_type_string(records[i-1].type),
+ wrepl_name_state_string(records[i-1].state),
+ (records[i-1].is_static?",static":""),
+ wrepl_name_type_string(records[i].type),
+ wrepl_name_state_string(records[i].state),
+ (records[i].is_static?",static":""),
+ (records[i-1].ips==records[i].ips?"same":"different"),
+ "REPLACE");
+ }
+
+ wins_name_cur->name = &names[j];
+ wins_name_cur->flags = WREPL_NAME_FLAGS(records[i].type,
+ records[i].state,
+ records[i].node,
+ records[i].is_static);
+ wins_name_cur->id = ++ctx->a.max_version;
+ if (wins_name_cur->flags & 2) {
+ wins_name_cur->addresses.addresses.num_ips = records[i].num_ips;
+ wins_name_cur->addresses.addresses.ips = discard_const_p(struct wrepl_ip,
+ records[i].ips);
+ } else {
+ wins_name_cur->addresses.ip = records[i].ips[0].ip;
+ }
+ wins_name_cur->unknown = "255.255.255.255";
+
+ ret &= test_wrepl_update_one(tctx, ctx, &ctx->a,wins_name_cur);
+ if (records[i].state == WREPL_STATE_RELEASED) {
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->a, wins_name_last, false);
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->a, wins_name_cur, false);
+ } else {
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->a, wins_name_cur, true);
+ }
+
+ /* the first one is a cleanup run */
+ if (!ret && i == 0) ret = true;
+
+ if (!ret) {
+ torture_comment(tctx, "conflict handled wrong or record[%u]: %s\n", i, __location__);
+ return ret;
+ }
+ }
+ }
+ return ret;
+}
+
+static bool test_conflict_different_owner(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx)
+{
+ bool ret = true;
+ struct wrepl_wins_name wins_name1;
+ struct wrepl_wins_name wins_name2;
+ struct wrepl_wins_name *wins_name_r1;
+ struct wrepl_wins_name *wins_name_r2;
+ uint32_t i;
+ struct {
+ const char *line; /* just better debugging */
+ struct nbt_name name;
+ const char *comment;
+ bool extra; /* not the worst case, this is an extra test */
+ bool cleanup;
+ struct {
+ struct wrepl_wins_owner *owner;
+ enum wrepl_name_type type;
+ enum wrepl_name_state state;
+ enum wrepl_name_node node;
+ bool is_static;
+ uint32_t num_ips;
+ const struct wrepl_ip *ips;
+ bool apply_expected;
+ bool sgroup_merge;
+ struct wrepl_wins_owner *merge_owner;
+ bool sgroup_cleanup;
+ } r1, r2;
+ } records[] = {
+ /*
+ * NOTE: the first record and the last applied one
+ * needs to be from the same owner,
+ * to not conflict in the next smbtorture run!!!
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true /* ignored */
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true /* ignored */
+ }
+ },
+
+/*
+ * unique vs unique section
+ */
+ /*
+ * unique,active vs. unique,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,active vs. unique,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * unique,released vs. unique,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,released vs. unique,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,tombstone vs. unique,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,tombstone vs. unique,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+
+/*
+ * unique vs normal groups section,
+ */
+ /*
+ * unique,active vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,active vs. group,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * unique,released vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,released vs. group,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,tombstone vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,tombstone vs. group,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * unique vs special groups section,
+ */
+ /*
+ * unique,active vs. sgroup,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * unique,active vs. sgroup,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * unique,released vs. sgroup,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,released vs. sgroup,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,tombstone vs. sgroup,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,tombstone vs. sgroup,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * unique vs multi homed section,
+ */
+ /*
+ * unique,active vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,active vs. mhomed,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * unique,released vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,released vs. mhomed,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,tombstone vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * unique,tombstone vs. mhomed,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * normal groups vs unique section,
+ */
+ /*
+ * group,active vs. unique,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,active vs. unique,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,released vs. unique,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,released vs. unique,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,tombstone vs. unique,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,tombstone vs. unique,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+/*
+ * normal groups vs normal groups section,
+ */
+ /*
+ * group,active vs. group,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,active vs. group,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,released vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * group,released vs. group,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * group,tombstone vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * group,tombstone vs. group,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * normal groups vs special groups section,
+ */
+ /*
+ * group,active vs. sgroup,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,active vs. sgroup,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,released vs. sgroup,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * group,released vs. sgroup,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,tombstone vs. sgroup,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * group,tombstone vs. sgroup,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * normal groups vs multi homed section,
+ */
+ /*
+ * group,active vs. mhomed,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,active vs. mhomed,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,released vs. mhomed,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,released vs. mhomed,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * group,tombstone vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * group,tombstone vs. mhomed,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * special groups vs unique section,
+ */
+ /*
+ * sgroup,active vs. unique,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * sgroup,active vs. unique,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * sgroup,released vs. unique,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,released vs. unique,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,tombstone vs. unique,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,tombstone vs. unique,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * special groups vs normal group section,
+ */
+ /*
+ * sgroup,active vs. group,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * sgroup,active vs. group,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * sgroup,released vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,released vs. group,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,tombstone vs. group,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,tombstone vs. group,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * special groups (not active) vs special group section,
+ */
+ /*
+ * sgroup,released vs. sgroup,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,released vs. sgroup,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,tombstone vs. sgroup,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,tombstone vs. sgroup,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * special groups vs multi homed section,
+ */
+ /*
+ * sgroup,active vs. mhomed,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * sgroup,active vs. mhomed,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * sgroup,released vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,released vs. mhomed,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,tombstone vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * sgroup,tombstone vs. mhomed,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * multi homed vs. unique section,
+ */
+ /*
+ * mhomed,active vs. unique,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,active vs. unique,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * mhomed,released vs. unique,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,released vs. uinique,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,tombstone vs. unique,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,tombstone vs. uinique,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * multi homed vs. normal group section,
+ */
+ /*
+ * mhomed,active vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,active vs. group,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * mhomed,released vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,released vs. group,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,tombstone vs. group,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,tombstone vs. group,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * multi homed vs. special group section,
+ */
+ /*
+ * mhomed,active vs. sgroup,active
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * mhomed,active vs. sgroup,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * mhomed,released vs. sgroup,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,released vs. sgroup,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,tombstone vs. sgroup,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,tombstone vs. sgroup,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ },
+
+/*
+ * multi homed vs. mlti homed section,
+ */
+ /*
+ * mhomed,active vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,active vs. mhomed,tombstone
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ }
+ },
+
+ /*
+ * mhomed,released vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,released vs. mhomed,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_RELEASED,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = false
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,tombstone vs. mhomed,active
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ }
+ },
+
+ /*
+ * mhomed,tombstone vs. mhomed,tombstone
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ }
+ },
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true,
+ }
+ },
+/*
+ * special group vs special group section,
+ */
+ /*
+ * sgroup,active vs. sgroup,active same addresses
+ * => should be NOT replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:A_3_4 vs. B:A_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = false,
+ .sgroup_cleanup = true
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active same addresses
+ * => should be NOT replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:A_3_4 vs. B:NULL",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ .sgroup_cleanup = true
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active subset addresses, special case...
+ * => should NOT be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:A_3_4_X_3_4 vs. B:A_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4_X_3_4),
+ .ips = addresses_A_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = false,
+ }
+ },
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ },
+ .r2 = {
+ .owner = &ctx->x,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses, but owner changed
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:B_3_4 vs. B:A_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true,
+ .sgroup_cleanup = true
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses, but owner changed
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:A_3_4 vs. B:A_3_4_OWNER_B",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4_OWNER_B),
+ .ips = addresses_A_3_4_OWNER_B,
+ .apply_expected = true,
+ .sgroup_cleanup = true
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses, but owner changed
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:A_3_4_OWNER_B vs. B:A_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4_OWNER_B),
+ .ips = addresses_A_3_4_OWNER_B,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true,
+ .sgroup_cleanup = true
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses
+ * => should be merged
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .sgroup_merge = true,
+ .sgroup_cleanup = true,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses, special case...
+ * => should be merged
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4_X_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4),
+ .ips = addresses_B_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .sgroup_merge = true,
+ .merge_owner = &ctx->b,
+ .sgroup_cleanup = false
+ }
+ },
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4_X_3_4_OWNER_B),
+ .ips = addresses_A_3_4_X_3_4_OWNER_B,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses, special case...
+ * => should be merged
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_X_3_4),
+ .ips = addresses_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .sgroup_merge = true,
+ .sgroup_cleanup = false
+ }
+ },
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ },
+ .r2 = {
+ .owner = &ctx->x,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses, special case...
+ * => should be merged
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:A_3_4_X_3_4 vs. B:A_3_4_OWNER_B => B:A_3_4_OWNER_B_X_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4_X_3_4),
+ .ips = addresses_A_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4_OWNER_B),
+ .ips = addresses_A_3_4_OWNER_B,
+ .sgroup_merge = true,
+ .merge_owner = &ctx->b,
+ }
+ },
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ },
+ .r2 = {
+ .owner = &ctx->x,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active partly different addresses, special case...
+ * => should be merged
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:B_3_4_X_3_4 vs. B:B_3_4_X_1_2 => C:B_3_4_X_1_2_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4),
+ .ips = addresses_B_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_1_2),
+ .ips = addresses_B_3_4_X_1_2,
+ .sgroup_merge = true,
+ .sgroup_cleanup = false
+ }
+ },
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ },
+ .r2 = {
+ .owner = &ctx->x,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses, special case...
+ * => should be merged
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:A_3_4_B_3_4 vs. B:NULL => B:A_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4_B_3_4),
+ .ips = addresses_A_3_4_B_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .sgroup_merge = true,
+ .merge_owner = &ctx->b,
+ .sgroup_cleanup = true
+ }
+ },
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active different addresses, special case...
+ * => should be merged
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4),
+ .ips = addresses_B_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .sgroup_merge = true,
+ .merge_owner = &ctx->b,
+ .sgroup_cleanup = true
+ }
+ },
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->x,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = false,
+ },
+ .r2 = {
+ .owner = &ctx->x,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true,
+ }
+ },
+
+ /*
+ * sgroup,active vs. sgroup,tombstone different no addresses, special
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:B_3_4_X_3_4 vs. B:NULL => B:NULL",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4),
+ .ips = addresses_B_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = 0,
+ .ips = NULL,
+ .apply_expected = true,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,tombstone different addresses
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4),
+ .ips = addresses_B_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ .apply_expected = true,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,tombstone subset addresses
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:B_3_4_X_3_4 vs. B:B_3_4 => B:B_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4),
+ .ips = addresses_B_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true,
+ }
+ },
+ /*
+ * sgroup,active vs. sgroup,active same addresses
+ * => should be replaced
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .comment= "A:B_3_4_X_3_4 vs. B:B_3_4_X_3_4 => B:B_3_4_X_3_4",
+ .extra = true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4),
+ .ips = addresses_B_3_4_X_3_4,
+ .apply_expected = true,
+ },
+ .r2 = {
+ .owner = &ctx->b,
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4_X_3_4),
+ .ips = addresses_B_3_4_X_3_4,
+ .apply_expected = true,
+ }
+ },
+
+ /*
+ * This should be the last record in this array,
+ * we need to make sure the we leave a tombstoned unique entry
+ * owned by OWNER_A
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_DIFF_OWNER", 0x00, NULL),
+ .cleanup= true,
+ .r1 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ },
+ .r2 = {
+ .owner = &ctx->a,
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_A_1),
+ .ips = addresses_A_1,
+ .apply_expected = true
+ }
+ }}; /* do not add entries here, this should be the last record! */
+
+ wins_name_r1 = &wins_name1;
+ wins_name_r2 = &wins_name2;
+
+ torture_comment(tctx, "Test Replica Conflicts with different owners\n");
+
+ for(i=0; ret && i < ARRAY_SIZE(records); i++) {
+
+ if (!records[i].extra && !records[i].cleanup) {
+ /* we should test the worst cases */
+ if (records[i].r2.apply_expected && records[i].r1.ips==records[i].r2.ips) {
+ torture_comment(tctx, "(%s) Programmer error, invalid record[%u]: %s\n",
+ __location__, i, records[i].line);
+ return false;
+ } else if (!records[i].r2.apply_expected && records[i].r1.ips!=records[i].r2.ips) {
+ torture_comment(tctx, "(%s) Programmer error, invalid record[%u]: %s\n",
+ __location__, i, records[i].line);
+ return false;
+ }
+ }
+
+ if (!records[i].cleanup) {
+ const char *expected;
+ const char *ips;
+
+ if (records[i].r2.sgroup_merge) {
+ expected = "SGROUP_MERGE";
+ } else if (records[i].r2.apply_expected) {
+ expected = "REPLACE";
+ } else {
+ expected = "NOT REPLACE";
+ }
+
+ if (!records[i].r1.ips && !records[i].r2.ips) {
+ ips = "with no ip(s)";
+ } else if (records[i].r1.ips==records[i].r2.ips) {
+ ips = "with same ip(s)";
+ } else {
+ ips = "with different ip(s)";
+ }
+
+ torture_comment(tctx, "%s,%s%s vs. %s,%s%s %s => %s\n",
+ wrepl_name_type_string(records[i].r1.type),
+ wrepl_name_state_string(records[i].r1.state),
+ (records[i].r1.is_static?",static":""),
+ wrepl_name_type_string(records[i].r2.type),
+ wrepl_name_state_string(records[i].r2.state),
+ (records[i].r2.is_static?",static":""),
+ (records[i].comment?records[i].comment:ips),
+ expected);
+ }
+
+ /*
+ * Setup R1
+ */
+ wins_name_r1->name = &records[i].name;
+ wins_name_r1->flags = WREPL_NAME_FLAGS(records[i].r1.type,
+ records[i].r1.state,
+ records[i].r1.node,
+ records[i].r1.is_static);
+ wins_name_r1->id = ++records[i].r1.owner->max_version;
+ if (wins_name_r1->flags & 2) {
+ wins_name_r1->addresses.addresses.num_ips = records[i].r1.num_ips;
+ wins_name_r1->addresses.addresses.ips = discard_const_p(struct wrepl_ip,
+ records[i].r1.ips);
+ } else {
+ wins_name_r1->addresses.ip = records[i].r1.ips[0].ip;
+ }
+ wins_name_r1->unknown = "255.255.255.255";
+
+ /* now apply R1 */
+ ret &= test_wrepl_update_one(tctx, ctx, records[i].r1.owner, wins_name_r1);
+ ret &= test_wrepl_is_applied(tctx, ctx, records[i].r1.owner,
+ wins_name_r1, records[i].r1.apply_expected);
+
+ /*
+ * Setup R2
+ */
+ wins_name_r2->name = &records[i].name;
+ wins_name_r2->flags = WREPL_NAME_FLAGS(records[i].r2.type,
+ records[i].r2.state,
+ records[i].r2.node,
+ records[i].r2.is_static);
+ wins_name_r2->id = ++records[i].r2.owner->max_version;
+ if (wins_name_r2->flags & 2) {
+ wins_name_r2->addresses.addresses.num_ips = records[i].r2.num_ips;
+ wins_name_r2->addresses.addresses.ips = discard_const_p(struct wrepl_ip,
+ records[i].r2.ips);
+ } else {
+ wins_name_r2->addresses.ip = records[i].r2.ips[0].ip;
+ }
+ wins_name_r2->unknown = "255.255.255.255";
+
+ /* now apply R2 */
+ ret &= test_wrepl_update_one(tctx, ctx, records[i].r2.owner, wins_name_r2);
+ if (records[i].r1.state == WREPL_STATE_RELEASED) {
+ ret &= test_wrepl_is_applied(tctx, ctx, records[i].r1.owner,
+ wins_name_r1, false);
+ } else if (records[i].r2.sgroup_merge) {
+ ret &= test_wrepl_sgroup_merged(tctx, ctx, records[i].r2.merge_owner,
+ records[i].r1.owner,
+ records[i].r1.num_ips, records[i].r1.ips,
+ records[i].r2.owner,
+ records[i].r2.num_ips, records[i].r2.ips,
+ wins_name_r2);
+ } else if (records[i].r1.owner != records[i].r2.owner) {
+ bool _expected;
+ _expected = (records[i].r1.apply_expected && !records[i].r2.apply_expected);
+ ret &= test_wrepl_is_applied(tctx, ctx, records[i].r1.owner,
+ wins_name_r1, _expected);
+ }
+ if (records[i].r2.state == WREPL_STATE_RELEASED) {
+ ret &= test_wrepl_is_applied(tctx, ctx, records[i].r2.owner,
+ wins_name_r2, false);
+ } else if (!records[i].r2.sgroup_merge) {
+ ret &= test_wrepl_is_applied(tctx, ctx, records[i].r2.owner,
+ wins_name_r2, records[i].r2.apply_expected);
+ }
+
+ if (records[i].r2.sgroup_cleanup) {
+ if (!ret) {
+ torture_comment(tctx, "failed before sgroup_cleanup record[%u]: %s\n", i, records[i].line);
+ return ret;
+ }
+
+ /* clean up the SGROUP record */
+ wins_name_r1->name = &records[i].name;
+ wins_name_r1->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP,
+ WREPL_STATE_ACTIVE,
+ WREPL_NODE_B, false);
+ wins_name_r1->id = ++records[i].r1.owner->max_version;
+ wins_name_r1->addresses.addresses.num_ips = 0;
+ wins_name_r1->addresses.addresses.ips = NULL;
+ wins_name_r1->unknown = "255.255.255.255";
+ ret &= test_wrepl_update_one(tctx, ctx, records[i].r1.owner, wins_name_r1);
+
+ /* here we test how names from an owner are deleted */
+ if (records[i].r2.sgroup_merge && records[i].r2.num_ips) {
+ ret &= test_wrepl_sgroup_merged(tctx, ctx, NULL,
+ records[i].r2.owner,
+ records[i].r2.num_ips, records[i].r2.ips,
+ records[i].r1.owner,
+ 0, NULL,
+ wins_name_r2);
+ }
+
+ /* clean up the SGROUP record */
+ wins_name_r2->name = &records[i].name;
+ wins_name_r2->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP,
+ WREPL_STATE_ACTIVE,
+ WREPL_NODE_B, false);
+ wins_name_r2->id = ++records[i].r2.owner->max_version;
+ wins_name_r2->addresses.addresses.num_ips = 0;
+ wins_name_r2->addresses.addresses.ips = NULL;
+ wins_name_r2->unknown = "255.255.255.255";
+ ret &= test_wrepl_update_one(tctx, ctx, records[i].r2.owner, wins_name_r2);
+
+ /* take ownership of the SGROUP record */
+ wins_name_r2->name = &records[i].name;
+ wins_name_r2->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP,
+ WREPL_STATE_ACTIVE,
+ WREPL_NODE_B, false);
+ wins_name_r2->id = ++records[i].r2.owner->max_version;
+ wins_name_r2->addresses.addresses.num_ips = ARRAY_SIZE(addresses_B_1);
+ wins_name_r2->addresses.addresses.ips = discard_const_p(struct wrepl_ip,
+ addresses_B_1);
+ wins_name_r2->unknown = "255.255.255.255";
+ ret &= test_wrepl_update_one(tctx, ctx, records[i].r2.owner, wins_name_r2);
+ ret &= test_wrepl_is_applied(tctx, ctx, records[i].r2.owner, wins_name_r2, true);
+
+ /* overwrite the SGROUP record with unique,tombstone */
+ wins_name_r2->name = &records[i].name;
+ wins_name_r2->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP,
+ WREPL_STATE_TOMBSTONE,
+ WREPL_NODE_B, false);
+ wins_name_r2->id = ++records[i].r2.owner->max_version;
+ wins_name_r2->addresses.addresses.num_ips = ARRAY_SIZE(addresses_B_1);
+ wins_name_r2->addresses.addresses.ips = discard_const_p(struct wrepl_ip,
+ addresses_B_1);
+ wins_name_r2->unknown = "255.255.255.255";
+ ret &= test_wrepl_update_one(tctx, ctx, records[i].r2.owner, wins_name_r2);
+ ret &= test_wrepl_is_applied(tctx, ctx, records[i].r2.owner, wins_name_r2, true);
+
+ if (!ret) {
+ torture_comment(tctx, "failed in sgroup_cleanup record[%u]: %s\n", i, records[i].line);
+ return ret;
+ }
+ }
+
+ /* the first one is a cleanup run */
+ if (!ret && i == 0) ret = true;
+
+ if (!ret) {
+ torture_comment(tctx, "conflict handled wrong or record[%u]: %s\n", i, records[i].line);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_conflict_owned_released_vs_replica(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ struct wrepl_wins_name wins_name_;
+ struct wrepl_wins_name *wins_name = &wins_name_;
+ struct nbt_name_register name_register_;
+ struct nbt_name_register *name_register = &name_register_;
+ struct nbt_name_release release_;
+ struct nbt_name_release *release = &release_;
+ uint32_t i;
+ struct {
+ const char *line; /* just better debugging */
+ struct nbt_name name;
+ struct {
+ uint32_t nb_flags;
+ bool mhomed;
+ uint32_t num_ips;
+ const struct wrepl_ip *ips;
+ bool apply_expected;
+ } wins;
+ struct {
+ enum wrepl_name_type type;
+ enum wrepl_name_state state;
+ enum wrepl_name_node node;
+ bool is_static;
+ uint32_t num_ips;
+ const struct wrepl_ip *ips;
+ bool apply_expected;
+ } replica;
+ } records[] = {
+/*
+ * unique vs. unique section
+ */
+ /*
+ * unique,released vs. unique,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_UA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. unique,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_UA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. unique,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_UT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. unique,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_UT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * unique vs. group section
+ */
+ /*
+ * unique,released vs. group,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_GA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. group,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_GA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. group,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_GT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. group,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_GT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * unique vs. special group section
+ */
+ /*
+ * unique,released vs. sgroup,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_SA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. sgroup,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_SA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. sgroup,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_ST_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. sgroup,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_ST_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * unique vs. multi homed section
+ */
+ /*
+ * unique,released vs. mhomed,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_MA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. mhomed,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_MA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. mhomed,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_MT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,released vs. mhomed,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_UR_MT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * group vs. unique section
+ */
+ /*
+ * group,released vs. unique,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_UA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. unique,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_UA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. unique,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_UT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. unique,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_UT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * group vs. group section
+ */
+ /*
+ * group,released vs. group,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_GA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * group,released vs. group,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_GA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * group,released vs. group,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_GT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * group,released vs. group,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_GT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * group vs. special group section
+ */
+ /*
+ * group,released vs. sgroup,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_SA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. sgroup,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_SA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. sgroup,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_ST_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. sgroup,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_ST_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * group vs. multi homed section
+ */
+ /*
+ * group,released vs. mhomed,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_MA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. mhomed,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_MA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. mhomed,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_MT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,released vs. mhomed,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_GR_MT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * special group vs. unique section
+ */
+ /*
+ * sgroup,released vs. unique,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_UA_SI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. unique,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_UA_DI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. unique,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_UT_SI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. unique,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_UT_DI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * special group vs. group section
+ */
+ /*
+ * sgroup,released vs. group,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_GA_SI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. group,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_GA_DI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. group,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_GT_SI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. group,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_GT_DI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * special group vs. special group section
+ */
+ /*
+ * sgroup,released vs. sgroup,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_SA_SI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. sgroup,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_SA_DI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. sgroup,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_ST_SI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. sgroup,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_ST_DI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * special group vs. multi homed section
+ */
+ /*
+ * sgroup,released vs. mhomed,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_MA_SI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. mhomed,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_MA_DI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. mhomed,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_MT_SI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * sgroup,released vs. mhomed,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_SR_MT_DI", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * multi homed vs. unique section
+ */
+ /*
+ * mhomed,released vs. unique,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_UA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. unique,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_UA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. unique,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_UT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. unique,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_UT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * multi homed vs. group section
+ */
+ /*
+ * mhomed,released vs. group,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_GA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. group,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_GA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. group,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_GT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. group,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_GT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * multi homed vs. special group section
+ */
+ /*
+ * mhomed,released vs. sgroup,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_SA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. sgroup,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_SA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. sgroup,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_ST_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. sgroup,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_ST_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+/*
+ * multi homed vs. multi homed section
+ */
+ /*
+ * mhomed,released vs. mhomed,active with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_MA_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. mhomed,active with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_MA_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. mhomed,tombstone with same ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_MT_SI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,released vs. mhomed,tombstone with different ip(s)
+ */
+ {
+ .line = __location__,
+ .name = _NBT_NAME("_MR_MT_DI", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ };
+
+ torture_comment(tctx, "Test Replica records vs. owned released records\n");
+
+ for(i=0; ret && i < ARRAY_SIZE(records); i++) {
+ torture_comment(tctx, "%s => %s\n", nbt_name_string(ctx, &records[i].name),
+ (records[i].replica.apply_expected?"REPLACE":"NOT REPLACE"));
+
+ /*
+ * Setup Register
+ */
+ name_register->in.name = records[i].name;
+ name_register->in.dest_addr = ctx->address;
+ name_register->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ name_register->in.address = records[i].wins.ips[0].ip;
+ name_register->in.nb_flags = records[i].wins.nb_flags;
+ name_register->in.register_demand= false;
+ name_register->in.broadcast = false;
+ name_register->in.multi_homed = records[i].wins.mhomed;
+ name_register->in.ttl = 300000;
+ name_register->in.timeout = 70;
+ name_register->in.retries = 0;
+
+ status = nbt_name_register(ctx->nbtsock, ctx, name_register);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_comment(tctx, "No response from %s for name register\n", ctx->address);
+ ret = false;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Bad response from %s for name register - %s\n",
+ ctx->address, nt_errstr(status));
+ ret = false;
+ }
+ CHECK_VALUE(tctx, name_register->out.rcode, 0);
+ CHECK_VALUE_STRING(tctx, name_register->out.reply_from, ctx->address);
+ CHECK_VALUE(tctx, name_register->out.name.type, records[i].name.type);
+ CHECK_VALUE_STRING(tctx, name_register->out.name.name, records[i].name.name);
+ CHECK_VALUE_STRING(tctx, name_register->out.name.scope, records[i].name.scope);
+ CHECK_VALUE_STRING(tctx, name_register->out.reply_addr, records[i].wins.ips[0].ip);
+
+ /* release the record */
+ release->in.name = records[i].name;
+ release->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ release->in.dest_addr = ctx->address;
+ release->in.address = records[i].wins.ips[0].ip;
+ release->in.nb_flags = records[i].wins.nb_flags;
+ release->in.broadcast = false;
+ release->in.timeout = 30;
+ release->in.retries = 0;
+
+ status = nbt_name_release(ctx->nbtsock, ctx, release);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_comment(tctx, "No response from %s for name release\n", ctx->address);
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Bad response from %s for name query - %s\n",
+ ctx->address, nt_errstr(status));
+ return false;
+ }
+ CHECK_VALUE(tctx, release->out.rcode, 0);
+
+ /*
+ * Setup Replica
+ */
+ wins_name->name = &records[i].name;
+ wins_name->flags = WREPL_NAME_FLAGS(records[i].replica.type,
+ records[i].replica.state,
+ records[i].replica.node,
+ records[i].replica.is_static);
+ wins_name->id = ++ctx->b.max_version;
+ if (wins_name->flags & 2) {
+ wins_name->addresses.addresses.num_ips = records[i].replica.num_ips;
+ wins_name->addresses.addresses.ips = discard_const_p(struct wrepl_ip,
+ records[i].replica.ips);
+ } else {
+ wins_name->addresses.ip = records[i].replica.ips[0].ip;
+ }
+ wins_name->unknown = "255.255.255.255";
+
+ ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name);
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name,
+ records[i].replica.apply_expected);
+
+ if (records[i].replica.apply_expected) {
+ wins_name->name = &records[i].name;
+ wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_UNIQUE,
+ WREPL_STATE_TOMBSTONE,
+ WREPL_NODE_B, false);
+ wins_name->id = ++ctx->b.max_version;
+ wins_name->addresses.ip = addresses_B_1[0].ip;
+ wins_name->unknown = "255.255.255.255";
+
+ ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name);
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, true);
+ } else {
+ release->in.name = records[i].name;
+ release->in.dest_addr = ctx->address;
+ release->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ release->in.address = records[i].wins.ips[0].ip;
+ release->in.nb_flags = records[i].wins.nb_flags;
+ release->in.broadcast = false;
+ release->in.timeout = 30;
+ release->in.retries = 0;
+
+ status = nbt_name_release(ctx->nbtsock, ctx, release);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_comment(tctx, "No response from %s for name release\n", ctx->address);
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Bad response from %s for name query - %s\n",
+ ctx->address, nt_errstr(status));
+ return false;
+ }
+ CHECK_VALUE(tctx, release->out.rcode, 0);
+ }
+ if (!ret) {
+ torture_comment(tctx, "conflict handled wrong or record[%u]: %s\n", i, records[i].line);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+struct test_conflict_owned_active_vs_replica_struct {
+ struct torture_context *tctx;
+ const char *line; /* just better debugging */
+ const char *section; /* just better debugging */
+ struct nbt_name name;
+ const char *comment;
+ bool skip;
+ struct {
+ uint32_t nb_flags;
+ bool mhomed;
+ uint32_t num_ips;
+ const struct wrepl_ip *ips;
+ bool apply_expected;
+ } wins;
+ struct {
+ uint32_t timeout;
+ bool positive;
+ bool expect_release;
+ bool late_release;
+ bool ret;
+ /* when num_ips == 0, then .wins.ips are used */
+ uint32_t num_ips;
+ const struct wrepl_ip *ips;
+ } defend;
+ struct {
+ enum wrepl_name_type type;
+ enum wrepl_name_state state;
+ enum wrepl_name_node node;
+ bool is_static;
+ uint32_t num_ips;
+ const struct wrepl_ip *ips;
+ bool apply_expected;
+ bool mhomed_merge;
+ bool sgroup_merge;
+ } replica;
+};
+
+static void test_conflict_owned_active_vs_replica_handler(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *req_packet,
+ struct socket_address *src);
+
+static bool test_conflict_owned_active_vs_replica(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ struct wrepl_wins_name wins_name_;
+ struct wrepl_wins_name *wins_name = &wins_name_;
+ struct nbt_name_register name_register_;
+ struct nbt_name_register *name_register = &name_register_;
+ struct nbt_name_release release_;
+ struct nbt_name_release *release = &release_;
+ uint32_t i;
+ struct test_conflict_owned_active_vs_replica_struct records[] = {
+/*
+ * unique vs. unique section
+ */
+ /*
+ * unique,active vs. unique,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_UA_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. unique,active with different ip(s), positive response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_UA_DI_P", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * unique,active vs. unique,active with different ip(s), positive response other ips
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_UA_DI_O", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * unique,active vs. unique,active with different ip(s), negative response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_UA_DI_N", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = false,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. unique,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_UT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * unique,active vs. unique,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_UT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * unique vs. group section
+ */
+ /*
+ * unique,active vs. group,active with same ip(s), release expected
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_GA_SI_R", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .expect_release = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. group,active with different ip(s), release expected
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_GA_DI_R", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .expect_release = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. group,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_GT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * unique,active vs. group,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_GT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * unique vs. special group section
+ */
+ /*
+ * unique,active vs. sgroup,active with same ip(s), release expected
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_SA_SI_R", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .expect_release = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. group,active with different ip(s), release expected
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_SA_DI_R", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .expect_release = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. sgroup,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_ST_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * unique,active vs. sgroup,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_ST_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * unique vs. multi homed section
+ */
+ /*
+ * unique,active vs. mhomed,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_MA_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. mhomed,active with superset ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_MA_SP_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_all_num,
+ .ips = ctx->addresses_all,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. mhomed,active with different ip(s), positive response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_MA_DI_P", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+ /*
+ * unique,active vs. mhomed,active with different ip(s), positive response other ips
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_MA_DI_O", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+ /*
+ * unique,active vs. mhomed,active with different ip(s), negative response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_MA_DI_N", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = false,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ },
+ },
+ /*
+ * unique,active vs. mhomed,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_MT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * unique,active vs. mhomed,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_MT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+/*
+ * normal group vs. unique section
+ */
+ /*
+ * group,active vs. unique,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_UA_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. unique,active with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_UA_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. unique,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_UT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. unique,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_UT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * normal group vs. normal group section
+ */
+ /*
+ * group,active vs. group,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_GA_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * group,active vs. group,active with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_GA_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * group,active vs. group,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_GT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. group,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_GT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * normal group vs. special group section
+ */
+ /*
+ * group,active vs. sgroup,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_SA_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. sgroup,active with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_SA_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. sgroup,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_ST_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. sgroup,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_ST_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+/*
+ * normal group vs. multi homed section
+ */
+ /*
+ * group,active vs. mhomed,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_MA_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. mhomed,active with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_MA_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. mhomed,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_MT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * group,active vs. mhomed,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_GA_MT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+/*
+ * special group vs. unique section
+ */
+ /*
+ * sgroup,active vs. unique,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_UA_SI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. unique,active with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_UA_DI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. unique,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_UT_SI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. unique,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_UT_DI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * special group vs. normal group section
+ */
+ /*
+ * sgroup,active vs. group,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_GA_SI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. group,active with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_GA_DI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. group,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_GT_SI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. group,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_GT_DI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * special group vs. multi homed section
+ */
+ /*
+ * sgroup,active vs. mhomed,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_MA_SI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. mhomed,active with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_MA_DI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. mhomed,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_MT_SI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. mhomed,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_MT_DI_U", 0x1C, NULL),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * multi homed vs. unique section
+ */
+ /*
+ * mhomed,active vs. unique,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_UA_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. unique,active with different ip(s), positive response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_UA_DI_P", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. unique,active with different ip(s), positive response other ips
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_UA_DI_O", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. unique,active with different ip(s), negative response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_UA_DI_N", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = false,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. unique,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_UT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. unique,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_UT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * multi homed vs. normal group section
+ */
+ /*
+ * mhomed,active vs. group,active with same ip(s), release expected
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_GA_SI_R", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .expect_release = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. group,active with different ip(s), release expected
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_GA_DI_R", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .expect_release = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. group,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_GT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. group,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_GT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_GROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * multi homed vs. special group section
+ */
+ /*
+ * mhomed,active vs. sgroup,active with same ip(s), release expected
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_SA_SI_R", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .expect_release = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. group,active with different ip(s), release expected
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_SA_DI_R", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .expect_release = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. sgroup,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_ST_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. sgroup,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_ST_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_1),
+ .ips = addresses_B_1,
+ .apply_expected = false
+ },
+ },
+/*
+ * multi homed vs. multi homed section
+ */
+ /*
+ * mhomed,active vs. mhomed,active with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with superset ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_SP_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_all_num,
+ .ips = ctx->addresses_all,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with different ip(s), positive response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_DI_P", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with different ip(s), positive response other ips
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_DI_O", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ARRAY_SIZE(addresses_A_3_4),
+ .ips = addresses_A_3_4,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with different ip(s), negative response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_DI_N", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = false,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,tombstone with same ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MT_SI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,tombstone with different ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MT_DI_U", 0x00, NULL),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+/*
+ * some more multi homed test, including merging
+ */
+ /*
+ * mhomed,active vs. mhomed,active with superset ip(s), unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .section= "Test Replica vs. owned active: some more MHOMED combinations",
+ .name = _NBT_NAME("_MA_MA_SP_U", 0x00, NULL),
+ .comment= "C:MHOMED vs. B:ALL => B:ALL",
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_all_num,
+ .ips = ctx->addresses_all,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with same ips, unchecked
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_SM_U", 0x00, NULL),
+ .comment= "C:MHOMED vs. B:MHOMED => B:MHOMED",
+ .skip = (ctx->addresses_mhomed_num < 2),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with subset ip(s), positive response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_SB_P", 0x00, NULL),
+ .comment= "C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED",
+ .skip = (ctx->addresses_mhomed_num < 2),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .mhomed_merge = true
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with subset ip(s), positive response, with all addresses
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_SB_A", 0x00, NULL),
+ .comment= "C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED",
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ctx->addresses_all_num,
+ .ips = ctx->addresses_all,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .mhomed_merge = true
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with subset ip(s), positive response, with replicas addresses
+ * TODO: check why the server sends a name release demand for one address?
+ * the release demand has no effect to the database record...
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_SB_PRA", 0x00, NULL),
+ .comment= "C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED",
+ .skip = (ctx->addresses_all_num < 2),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .late_release = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with subset ip(s), positive response, with other addresses
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_SB_O", 0x00, NULL),
+ .comment= "C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED",
+ .skip = (ctx->addresses_all_num < 2),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ /*
+ * mhomed,active vs. mhomed,active with subset ip(s), negative response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_MA_MA_SB_N", 0x00, NULL),
+ .comment= "C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST",
+ .skip = (ctx->addresses_mhomed_num < 2),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = false
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ },
+/*
+ * some more multi homed and unique test, including merging
+ */
+ /*
+ * mhomed,active vs. unique,active with subset ip(s), positive response
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .section= "Test Replica vs. owned active: some more UNIQUE,MHOMED combinations",
+ .name = _NBT_NAME("_MA_UA_SB_P", 0x00, NULL),
+ .comment= "C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED",
+ .skip = (ctx->addresses_all_num < 2),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = true,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .mhomed_merge = true
+ },
+ },
+ /*
+ * unique,active vs. unique,active with different ip(s), positive response, with replicas address
+ * TODO: check why the server sends a name release demand for one address?
+ * the release demand has no effect to the database record...
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_UA_DI_PRA", 0x00, NULL),
+ .comment= "C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST",
+ .skip = (ctx->addresses_all_num < 2),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ctx->addresses_best2_num,
+ .ips = ctx->addresses_best2,
+ .late_release = true
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best2_num,
+ .ips = ctx->addresses_best2,
+ .apply_expected = false,
+ },
+ },
+ /*
+ * unique,active vs. unique,active with different ip(s), positive response, with all addresses
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_UA_DI_A", 0x00, NULL),
+ .comment= "C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED",
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ctx->addresses_all_num,
+ .ips = ctx->addresses_all,
+ },
+ .replica= {
+ .type = WREPL_TYPE_UNIQUE,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best2_num,
+ .ips = ctx->addresses_best2,
+ .mhomed_merge = true,
+ },
+ },
+ /*
+ * unique,active vs. mhomed,active with different ip(s), positive response, with all addresses
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_UA_MA_DI_A", 0x00, NULL),
+ .comment= "C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED",
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = 0,
+ .mhomed = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 10,
+ .positive = true,
+ .num_ips = ctx->addresses_all_num,
+ .ips = ctx->addresses_all,
+ },
+ .replica= {
+ .type = WREPL_TYPE_MHOMED,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best2_num,
+ .ips = ctx->addresses_best2,
+ .mhomed_merge = true,
+ },
+ },
+/*
+ * special group vs. special group merging section
+ */
+ /*
+ * sgroup,active vs. sgroup,active with different ip(s)
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .section= "Test Replica vs. owned active: SGROUP vs. SGROUP tests",
+ .name = _NBT_NAME("_SA_SA_DI_U", 0x1C, NULL),
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .sgroup_merge = true
+ },
+ },
+ /*
+ * sgroup,active vs. sgroup,active with same ip(s)
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_SA_SI_U", 0x1C, NULL),
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .sgroup_merge = true
+ },
+ },
+ /*
+ * sgroup,active vs. sgroup,active with superset ip(s)
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_SA_SP_U", 0x1C, NULL),
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_all_num,
+ .ips = ctx->addresses_all,
+ .sgroup_merge = true
+ },
+ },
+ /*
+ * sgroup,active vs. sgroup,active with subset ip(s)
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_SA_SB_U", 0x1C, NULL),
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_ACTIVE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .sgroup_merge = true
+ },
+ },
+ /*
+ * sgroup,active vs. sgroup,tombstone with different ip(s)
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_ST_DI_U", 0x1C, NULL),
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ARRAY_SIZE(addresses_B_3_4),
+ .ips = addresses_B_3_4,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. sgroup,tombstone with same ip(s)
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_ST_SI_U", 0x1C, NULL),
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. sgroup,tombstone with superset ip(s)
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_ST_SP_U", 0x1C, NULL),
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_all_num,
+ .ips = ctx->addresses_all,
+ .apply_expected = false
+ },
+ },
+ /*
+ * sgroup,active vs. sgroup,tombstone with subset ip(s)
+ */
+ {
+ .tctx = tctx,
+ .line = __location__,
+ .name = _NBT_NAME("_SA_ST_SB_U", 0x1C, NULL),
+ .skip = (ctx->addresses_all_num < 3),
+ .wins = {
+ .nb_flags = NBT_NM_GROUP,
+ .mhomed = false,
+ .num_ips = ctx->addresses_mhomed_num,
+ .ips = ctx->addresses_mhomed,
+ .apply_expected = true
+ },
+ .defend = {
+ .timeout = 0,
+ },
+ .replica= {
+ .type = WREPL_TYPE_SGROUP,
+ .state = WREPL_STATE_TOMBSTONE,
+ .node = WREPL_NODE_B,
+ .is_static = false,
+ .num_ips = ctx->addresses_best_num,
+ .ips = ctx->addresses_best,
+ .apply_expected = false
+ },
+ },
+ };
+
+ if (!ctx->nbtsock_srv) {
+ torture_comment(tctx, "SKIP: Test Replica records vs. owned active records: not bound to port[%d]\n",
+ lpcfg_nbt_port(tctx->lp_ctx));
+ return true;
+ }
+
+ torture_comment(tctx, "Test Replica records vs. owned active records\n");
+
+ for(i=0; ret && i < ARRAY_SIZE(records); i++) {
+ struct timeval end;
+ struct test_conflict_owned_active_vs_replica_struct record = records[i];
+ uint32_t j, count = 1;
+ const char *action;
+
+ if (records[i].wins.mhomed || records[i].name.type == 0x1C) {
+ count = records[i].wins.num_ips;
+ }
+
+ if (records[i].section) {
+ torture_comment(tctx, "%s\n", records[i].section);
+ }
+
+ if (records[i].skip) {
+ torture_comment(tctx, "%s => SKIPPED\n", nbt_name_string(ctx, &records[i].name));
+ continue;
+ }
+
+ if (records[i].replica.mhomed_merge) {
+ action = "MHOMED_MERGE";
+ } else if (records[i].replica.sgroup_merge) {
+ action = "SGROUP_MERGE";
+ } else if (records[i].replica.apply_expected) {
+ action = "REPLACE";
+ } else {
+ action = "NOT REPLACE";
+ }
+
+ torture_comment(tctx, "%s%s%s => %s\n",
+ nbt_name_string(ctx, &records[i].name),
+ (records[i].comment?": ":""),
+ (records[i].comment?records[i].comment:""),
+ action);
+
+ /* Prepare for multi homed registration */
+ ZERO_STRUCT(records[i].defend);
+ records[i].defend.timeout = 10;
+ records[i].defend.positive = true;
+ nbt_set_incoming_handler(ctx->nbtsock_srv,
+ test_conflict_owned_active_vs_replica_handler,
+ &records[i]);
+ if (ctx->nbtsock_srv2) {
+ nbt_set_incoming_handler(ctx->nbtsock_srv2,
+ test_conflict_owned_active_vs_replica_handler,
+ &records[i]);
+ }
+
+ /*
+ * Setup Register
+ */
+ for (j=0; j < count; j++) {
+ struct nbt_name_request *req;
+
+ name_register->in.name = records[i].name;
+ name_register->in.dest_addr = ctx->address;
+ name_register->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ name_register->in.address = records[i].wins.ips[j].ip;
+ name_register->in.nb_flags = records[i].wins.nb_flags;
+ name_register->in.register_demand= false;
+ name_register->in.broadcast = false;
+ name_register->in.multi_homed = records[i].wins.mhomed;
+ name_register->in.ttl = 300000;
+ name_register->in.timeout = 70;
+ name_register->in.retries = 0;
+
+ req = nbt_name_register_send(ctx->nbtsock, name_register);
+
+ /* push the request on the wire */
+ tevent_loop_once(ctx->nbtsock->event_ctx);
+
+ /*
+ * if we register multiple addresses,
+ * the server will do name queries to see if the old addresses
+ * are still alive
+ */
+ if (records[i].wins.mhomed && j > 0) {
+ end = timeval_current_ofs(records[i].defend.timeout,0);
+ records[i].defend.ret = true;
+ while (records[i].defend.timeout > 0) {
+ tevent_loop_once(ctx->nbtsock_srv->event_ctx);
+ if (timeval_expired(&end)) break;
+ }
+ ret &= records[i].defend.ret;
+ }
+
+ status = nbt_name_register_recv(req, ctx, name_register);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_comment(tctx, "No response from %s for name register\n", ctx->address);
+ ret = false;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Bad response from %s for name register - %s\n",
+ ctx->address, nt_errstr(status));
+ ret = false;
+ }
+ CHECK_VALUE(tctx, name_register->out.rcode, 0);
+ CHECK_VALUE_STRING(tctx, name_register->out.reply_from, ctx->address);
+ CHECK_VALUE(tctx, name_register->out.name.type, records[i].name.type);
+ CHECK_VALUE_STRING(tctx, name_register->out.name.name, records[i].name.name);
+ CHECK_VALUE_STRING(tctx, name_register->out.name.scope, records[i].name.scope);
+ CHECK_VALUE_STRING(tctx, name_register->out.reply_addr, records[i].wins.ips[j].ip);
+ }
+
+ /* Prepare for the current test */
+ records[i].defend = record.defend;
+ nbt_set_incoming_handler(ctx->nbtsock_srv,
+ test_conflict_owned_active_vs_replica_handler,
+ &records[i]);
+ if (ctx->nbtsock_srv2) {
+ nbt_set_incoming_handler(ctx->nbtsock_srv2,
+ test_conflict_owned_active_vs_replica_handler,
+ &records[i]);
+ }
+
+ /*
+ * Setup Replica
+ */
+ wins_name->name = &records[i].name;
+ wins_name->flags = WREPL_NAME_FLAGS(records[i].replica.type,
+ records[i].replica.state,
+ records[i].replica.node,
+ records[i].replica.is_static);
+ wins_name->id = ++ctx->b.max_version;
+ if (wins_name->flags & 2) {
+ wins_name->addresses.addresses.num_ips = records[i].replica.num_ips;
+ wins_name->addresses.addresses.ips = discard_const_p(struct wrepl_ip,
+ records[i].replica.ips);
+ } else {
+ wins_name->addresses.ip = records[i].replica.ips[0].ip;
+ }
+ wins_name->unknown = "255.255.255.255";
+
+ ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name);
+
+ /*
+ * wait for the name query, which is handled in
+ * test_conflict_owned_active_vs_replica_handler()
+ */
+ end = timeval_current_ofs(records[i].defend.timeout,0);
+ records[i].defend.ret = true;
+ while (records[i].defend.timeout > 0) {
+ tevent_loop_once(ctx->nbtsock_srv->event_ctx);
+ if (timeval_expired(&end)) break;
+ }
+ ret &= records[i].defend.ret;
+
+ if (records[i].defend.late_release) {
+ records[i].defend = record.defend;
+ records[i].defend.expect_release = true;
+ /*
+ * wait for the name release demand, which is handled in
+ * test_conflict_owned_active_vs_replica_handler()
+ */
+ end = timeval_current_ofs(records[i].defend.timeout,0);
+ records[i].defend.ret = true;
+ while (records[i].defend.timeout > 0) {
+ tevent_loop_once(ctx->nbtsock_srv->event_ctx);
+ if (timeval_expired(&end)) break;
+ }
+ ret &= records[i].defend.ret;
+ }
+
+ if (records[i].replica.mhomed_merge) {
+ ret &= test_wrepl_mhomed_merged(tctx, ctx, &ctx->c,
+ records[i].wins.num_ips, records[i].wins.ips,
+ &ctx->b,
+ records[i].replica.num_ips, records[i].replica.ips,
+ wins_name);
+ } else if (records[i].replica.sgroup_merge) {
+ ret &= test_wrepl_sgroup_merged(tctx, ctx, NULL,
+ &ctx->c,
+ records[i].wins.num_ips, records[i].wins.ips,
+ &ctx->b,
+ records[i].replica.num_ips, records[i].replica.ips,
+ wins_name);
+ } else {
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name,
+ records[i].replica.apply_expected);
+ }
+
+ if (records[i].replica.apply_expected ||
+ records[i].replica.mhomed_merge) {
+ wins_name->name = &records[i].name;
+ wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_UNIQUE,
+ WREPL_STATE_TOMBSTONE,
+ WREPL_NODE_B, false);
+ wins_name->id = ++ctx->b.max_version;
+ wins_name->addresses.ip = addresses_B_1[0].ip;
+ wins_name->unknown = "255.255.255.255";
+
+ ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name);
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, true);
+ } else {
+ for (j=0; j < count; j++) {
+ struct nbt_name_socket *nbtsock = ctx->nbtsock;
+
+ if (ctx->myaddr2 && strcmp(records[i].wins.ips[j].ip, ctx->myaddr2->addr) == 0) {
+ nbtsock = ctx->nbtsock2;
+ }
+
+ release->in.name = records[i].name;
+ release->in.dest_addr = ctx->address;
+ release->in.dest_port = lpcfg_nbt_port(tctx->lp_ctx);
+ release->in.address = records[i].wins.ips[j].ip;
+ release->in.nb_flags = records[i].wins.nb_flags;
+ release->in.broadcast = false;
+ release->in.timeout = 30;
+ release->in.retries = 0;
+
+ status = nbt_name_release(nbtsock, ctx, release);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ torture_comment(tctx, "No response from %s for name release\n", ctx->address);
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Bad response from %s for name query - %s\n",
+ ctx->address, nt_errstr(status));
+ return false;
+ }
+ CHECK_VALUE(tctx, release->out.rcode, 0);
+ }
+
+ if (records[i].replica.sgroup_merge) {
+ /* clean up the SGROUP record */
+ wins_name->name = &records[i].name;
+ wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP,
+ WREPL_STATE_ACTIVE,
+ WREPL_NODE_B, false);
+ wins_name->id = ++ctx->b.max_version;
+ wins_name->addresses.addresses.num_ips = 0;
+ wins_name->addresses.addresses.ips = NULL;
+ wins_name->unknown = "255.255.255.255";
+ ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name);
+
+ /* take ownership of the SGROUP record */
+ wins_name->name = &records[i].name;
+ wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_SGROUP,
+ WREPL_STATE_ACTIVE,
+ WREPL_NODE_B, false);
+ wins_name->id = ++ctx->b.max_version;
+ wins_name->addresses.addresses.num_ips = ARRAY_SIZE(addresses_B_1);
+ wins_name->addresses.addresses.ips = discard_const_p(struct wrepl_ip,
+ addresses_B_1);
+ wins_name->unknown = "255.255.255.255";
+ ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name);
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, true);
+
+ /* overwrite the SGROUP record with unique,tombstone */
+ wins_name->name = &records[i].name;
+ wins_name->flags = WREPL_NAME_FLAGS(WREPL_TYPE_UNIQUE,
+ WREPL_STATE_TOMBSTONE,
+ WREPL_NODE_B, false);
+ wins_name->id = ++ctx->b.max_version;
+ wins_name->addresses.ip = addresses_A_1[0].ip;
+ wins_name->unknown = "255.255.255.255";
+ ret &= test_wrepl_update_one(tctx, ctx, &ctx->b, wins_name);
+ ret &= test_wrepl_is_applied(tctx, ctx, &ctx->b, wins_name, true);
+ }
+ }
+
+ if (!ret) {
+ torture_comment(tctx, "conflict handled wrong or record[%u]: %s\n", i, records[i].line);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+#define __NBT_LABEL_CAT1__(a,b) a##b
+#define __NBT_LABEL_CAT2__(a,b) __NBT_LABEL_CAT1__(a,b)
+#define _NBT_LABEL __NBT_LABEL_CAT2__(_label_, __LINE__)
+
+#define _NBT_ASSERT(v, correct) do { \
+ bool _ret = true; \
+ torture_assert_int_equal_goto(rec->tctx, v, correct, \
+ _ret, _NBT_LABEL, "Invalid int value"); \
+_NBT_LABEL: \
+ if (!_ret) { \
+ return; \
+ } \
+} while (0)
+
+#define _NBT_ASSERT_STRING(v, correct) do { \
+ bool _ret = true; \
+ torture_assert_str_equal_goto(rec->tctx, v, correct, \
+ _ret, _NBT_LABEL, "Invalid string value"); \
+_NBT_LABEL: \
+ if (!_ret) { \
+ return; \
+ } \
+} while (0)
+
+static void test_conflict_owned_active_vs_replica_handler_query(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *req_packet,
+ struct socket_address *src)
+{
+ struct nbt_name *name;
+ struct nbt_name_packet *rep_packet;
+ struct test_conflict_owned_active_vs_replica_struct *rec =
+ (struct test_conflict_owned_active_vs_replica_struct *)nbtsock->incoming.private_data;
+
+ _NBT_ASSERT(req_packet->qdcount, 1);
+ _NBT_ASSERT(req_packet->questions[0].question_type, NBT_QTYPE_NETBIOS);
+ _NBT_ASSERT(req_packet->questions[0].question_class, NBT_QCLASS_IP);
+
+ name = &req_packet->questions[0].name;
+
+ _NBT_ASSERT_STRING(name->name, rec->name.name);
+ _NBT_ASSERT(name->type, rec->name.type);
+ _NBT_ASSERT_STRING(name->scope, rec->name.scope);
+
+ _NBT_ASSERT(rec->defend.expect_release, false);
+
+ rep_packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (rep_packet == NULL) return;
+
+ rep_packet->name_trn_id = req_packet->name_trn_id;
+ rep_packet->ancount = 1;
+
+ rep_packet->answers = talloc_array(rep_packet, struct nbt_res_rec, 1);
+ if (rep_packet->answers == NULL) return;
+
+ rep_packet->answers[0].name = *name;
+ rep_packet->answers[0].rr_class = NBT_QCLASS_IP;
+ rep_packet->answers[0].ttl = 0;
+
+ if (rec->defend.positive) {
+ uint32_t i, num_ips;
+ const struct wrepl_ip *ips;
+
+ if (rec->defend.num_ips > 0) {
+ num_ips = rec->defend.num_ips;
+ ips = rec->defend.ips;
+ } else {
+ num_ips = rec->wins.num_ips;
+ ips = rec->wins.ips;
+ }
+
+ /* send a positive reply */
+ rep_packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_QUERY |
+ NBT_FLAG_AUTHORITATIVE |
+ NBT_FLAG_RECURSION_DESIRED |
+ NBT_FLAG_RECURSION_AVAIL;
+
+ rep_packet->answers[0].rr_type = NBT_QTYPE_NETBIOS;
+
+ rep_packet->answers[0].rdata.netbios.length = num_ips*6;
+ rep_packet->answers[0].rdata.netbios.addresses =
+ talloc_array(rep_packet->answers, struct nbt_rdata_address, num_ips);
+ if (rep_packet->answers[0].rdata.netbios.addresses == NULL) return;
+
+ for (i=0; i < num_ips; i++) {
+ struct nbt_rdata_address *addr =
+ &rep_packet->answers[0].rdata.netbios.addresses[i];
+ addr->nb_flags = rec->wins.nb_flags;
+ addr->ipaddr = ips[i].ip;
+ }
+ DEBUG(2,("Sending positive name query reply for %s to %s:%d\n",
+ nbt_name_string(rep_packet, name), src->addr, src->port));
+ } else {
+ /* send a negative reply */
+ rep_packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_QUERY |
+ NBT_FLAG_AUTHORITATIVE |
+ NBT_RCODE_NAM;
+
+ rep_packet->answers[0].rr_type = NBT_QTYPE_NULL;
+
+ ZERO_STRUCT(rep_packet->answers[0].rdata);
+
+ DEBUG(2,("Sending negative name query reply for %s to %s:%d\n",
+ nbt_name_string(rep_packet, name), src->addr, src->port));
+ }
+
+ nbt_name_reply_send(nbtsock, src, rep_packet);
+ talloc_free(rep_packet);
+
+ /* make sure we push the reply to the wire */
+ while (nbtsock->send_queue) {
+ tevent_loop_once(nbtsock->event_ctx);
+ }
+ smb_msleep(1000);
+
+ rec->defend.timeout = 0;
+ rec->defend.ret = true;
+}
+
+static void test_conflict_owned_active_vs_replica_handler_release(
+ struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *req_packet,
+ struct socket_address *src)
+{
+ struct nbt_name *name;
+ struct nbt_name_packet *rep_packet;
+ struct test_conflict_owned_active_vs_replica_struct *rec =
+ (struct test_conflict_owned_active_vs_replica_struct *)nbtsock->incoming.private_data;
+
+ _NBT_ASSERT(req_packet->qdcount, 1);
+ _NBT_ASSERT(req_packet->questions[0].question_type, NBT_QTYPE_NETBIOS);
+ _NBT_ASSERT(req_packet->questions[0].question_class, NBT_QCLASS_IP);
+
+ name = &req_packet->questions[0].name;
+
+ _NBT_ASSERT_STRING(name->name, rec->name.name);
+ _NBT_ASSERT(name->type, rec->name.type);
+ _NBT_ASSERT_STRING(name->scope, rec->name.scope);
+
+ _NBT_ASSERT(rec->defend.expect_release, true);
+
+ rep_packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (rep_packet == NULL) return;
+
+ rep_packet->name_trn_id = req_packet->name_trn_id;
+ rep_packet->ancount = 1;
+ rep_packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_RELEASE |
+ NBT_FLAG_AUTHORITATIVE;
+
+ rep_packet->answers = talloc_array(rep_packet, struct nbt_res_rec, 1);
+ if (rep_packet->answers == NULL) return;
+
+ rep_packet->answers[0].name = *name;
+ rep_packet->answers[0].rr_type = NBT_QTYPE_NETBIOS;
+ rep_packet->answers[0].rr_class = NBT_QCLASS_IP;
+ rep_packet->answers[0].ttl = req_packet->additional[0].ttl;
+ rep_packet->answers[0].rdata = req_packet->additional[0].rdata;
+
+ DEBUG(2,("Sending name release reply for %s to %s:%d\n",
+ nbt_name_string(rep_packet, name), src->addr, src->port));
+
+ nbt_name_reply_send(nbtsock, src, rep_packet);
+ talloc_free(rep_packet);
+
+ /* make sure we push the reply to the wire */
+ while (nbtsock->send_queue) {
+ tevent_loop_once(nbtsock->event_ctx);
+ }
+ smb_msleep(1000);
+
+ rec->defend.timeout = 0;
+ rec->defend.ret = true;
+}
+
+static void test_conflict_owned_active_vs_replica_handler(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *req_packet,
+ struct socket_address *src)
+{
+ struct test_conflict_owned_active_vs_replica_struct *rec =
+ (struct test_conflict_owned_active_vs_replica_struct *)nbtsock->incoming.private_data;
+ struct nbt_name *name = &req_packet->questions[0].name;
+
+ if (req_packet->operation & NBT_FLAG_BROADCAST) {
+ torture_comment(rec->tctx,
+ "%s: incoming packet name[%s] flags[0x%08X] from[%s]\n",
+ __location__,
+ nbt_name_string(rec->tctx, name),
+ req_packet->operation,
+ src->addr);
+ return;
+ }
+
+ rec->defend.ret = false;
+
+ switch (req_packet->operation & NBT_OPCODE) {
+ case NBT_OPCODE_QUERY:
+ test_conflict_owned_active_vs_replica_handler_query(nbtsock, req_packet, src);
+ break;
+ case NBT_OPCODE_RELEASE:
+ test_conflict_owned_active_vs_replica_handler_release(nbtsock, req_packet, src);
+ break;
+ default:
+ torture_comment(rec->tctx,
+ "%s: unexpected packet name[%s] flags[0x%08X] from[%s]\n",
+ __location__,
+ nbt_name_string(rec->tctx, name),
+ req_packet->operation,
+ src->addr);
+ _NBT_ASSERT((req_packet->operation & NBT_OPCODE), NBT_OPCODE_QUERY);
+ break;
+ }
+}
+
+/*
+ test WINS replication replica conflicts operations
+*/
+static bool torture_nbt_winsreplication_replica(struct torture_context *tctx)
+{
+ bool ret = true;
+ struct test_wrepl_conflict_conn *ctx;
+
+ const char *address;
+ struct nbt_name name;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ ctx = test_create_conflict_ctx(tctx, address);
+ if (!ctx) return false;
+
+ ret &= test_conflict_same_owner(tctx, ctx);
+ ret &= test_conflict_different_owner(tctx, ctx);
+
+ return ret;
+}
+
+/*
+ test WINS replication owned conflicts operations
+*/
+static bool torture_nbt_winsreplication_owned(struct torture_context *tctx)
+{
+ const char *address;
+ struct nbt_name name;
+ bool ret = true;
+ struct test_wrepl_conflict_conn *ctx;
+
+ if (torture_setting_bool(tctx, "quick", false))
+ torture_skip(tctx,
+ "skip NBT-WINSREPLICATION-OWNED test in quick test mode\n");
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ ctx = test_create_conflict_ctx(tctx, address);
+ torture_assert(tctx, ctx != NULL, "Creating context failed");
+
+ ret &= test_conflict_owned_released_vs_replica(tctx, ctx);
+ ret &= test_conflict_owned_active_vs_replica(tctx, ctx);
+
+ return ret;
+}
+
+/*
+ test simple WINS replication operations
+*/
+struct torture_suite *torture_nbt_winsreplication(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(
+ mem_ctx, "winsreplication");
+ struct torture_tcase *tcase;
+
+ tcase = torture_suite_add_simple_test(suite, "assoc_ctx1",
+ test_assoc_ctx1);
+ tcase->tests->dangerous = true;
+
+ torture_suite_add_simple_test(suite, "assoc_ctx2", test_assoc_ctx2);
+
+ torture_suite_add_simple_test(suite, "wins_replication",
+ test_wins_replication);
+
+ torture_suite_add_simple_test(suite, "replica",
+ torture_nbt_winsreplication_replica);
+
+ torture_suite_add_simple_test(suite, "owned",
+ torture_nbt_winsreplication_owned);
+
+ return suite;
+}