diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source4/torture/nbt | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/torture/nbt')
-rw-r--r-- | source4/torture/nbt/dgram.c | 699 | ||||
-rw-r--r-- | source4/torture/nbt/nbt.c | 69 | ||||
-rw-r--r-- | source4/torture/nbt/query.c | 115 | ||||
-rw-r--r-- | source4/torture/nbt/register.c | 176 | ||||
-rw-r--r-- | source4/torture/nbt/wins.c | 545 | ||||
-rw-r--r-- | source4/torture/nbt/winsbench.c | 300 | ||||
-rw-r--r-- | source4/torture/nbt/winsreplication.c | 9884 |
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..e1c9a4d --- /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), reason: %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), reason: %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 socketwrapper */ + 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 socketwrapper */ + 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,tombstone 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; +} |