Loading src/or/directory.c +0 −42 Original line number Diff line number Diff line Loading @@ -1012,48 +1012,6 @@ directory_must_use_begindir(const or_options_t *options) return !public_server_mode(options); } struct directory_request_t { /** * These fields specify which directory we're contacting. Routerstatus, * if present, overrides the other fields. * * @{ */ tor_addr_port_t or_addr_port; tor_addr_port_t dir_addr_port; char digest[DIGEST_LEN]; const routerstatus_t *routerstatus; /** @} */ /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what * kind of operation we'll be doing (upload/download), and of what kind * of document. */ uint8_t dir_purpose; /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo * and extrainfo docs. */ uint8_t router_purpose; /** Enum: determines whether to anonymize, and whether to use dirport or * orport. */ dir_indirection_t indirection; /** Alias to the variable part of the URL for this request */ const char *resource; /** Alias to the payload to upload (if any) */ const char *payload; /** Number of bytes to upload from payload</b> */ size_t payload_len; /** Value to send in an if-modified-since header, or 0 for none. */ time_t if_modified_since; /** Hidden-service-specific information v2. */ const rend_data_t *rend_query; /** Extra headers to append to the request */ config_line_t *additional_headers; /** Hidden-service-specific information for v3+. */ const hs_ident_dir_conn_t *hs_ident; /** Used internally to directory.c: gets informed when the attempt to * connect to the directory succeeds or fails, if that attempt bears on the * directory's usability as a directory guard. */ circuit_guard_state_t *guard_state; }; /** Evaluate the situation and decide if we should use an encrypted * "begindir-style" connection for this directory request. * 0) If there is no DirPort, yes. Loading src/or/directory.h +42 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,48 @@ typedef struct response_handler_args_t { const char *headers; } response_handler_args_t; struct directory_request_t { /** * These fields specify which directory we're contacting. Routerstatus, * if present, overrides the other fields. * * @{ */ tor_addr_port_t or_addr_port; tor_addr_port_t dir_addr_port; char digest[DIGEST_LEN]; const routerstatus_t *routerstatus; /** @} */ /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what * kind of operation we'll be doing (upload/download), and of what kind * of document. */ uint8_t dir_purpose; /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo * and extrainfo docs. */ uint8_t router_purpose; /** Enum: determines whether to anonymize, and whether to use dirport or * orport. */ dir_indirection_t indirection; /** Alias to the variable part of the URL for this request */ const char *resource; /** Alias to the payload to upload (if any) */ const char *payload; /** Number of bytes to upload from payload</b> */ size_t payload_len; /** Value to send in an if-modified-since header, or 0 for none. */ time_t if_modified_since; /** Hidden-service-specific information v2. */ const rend_data_t *rend_query; /** Extra headers to append to the request */ config_line_t *additional_headers; /** Hidden-service-specific information for v3+. */ const hs_ident_dir_conn_t *hs_ident; /** Used internally to directory.c: gets informed when the attempt to * connect to the directory succeeds or fails, if that attempt bears on the * directory's usability as a directory guard. */ struct circuit_guard_state_t *guard_state; }; struct get_handler_args_t; STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn, const struct get_handler_args_t *args); Loading src/test/test_entrynodes.c +129 −17 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #define STATEFILE_PRIVATE #define ENTRYNODES_PRIVATE #define ROUTERLIST_PRIVATE #define DIRECTORY_PRIVATE #include "or.h" #include "test.h" Loading @@ -15,6 +16,7 @@ #include "circuitlist.h" #include "config.h" #include "confparse.h" #include "directory.h" #include "entrynodes.h" #include "nodelist.h" #include "networkstatus.h" Loading Loading @@ -129,6 +131,14 @@ big_fake_network_setup(const struct testcase_t *testcase) n->rs->has_bandwidth = 1; n->rs->bandwidth_kb = 30; /* Make a random nickname for each node */ { char nickname_binary[8]; crypto_rand(nickname_binary, sizeof(nickname_binary)); base64_encode(n->rs->nickname, sizeof(n->rs->nickname), nickname_binary, sizeof(nickname_binary), 0); } /* Call half of the nodes a possible guard. */ if (i % 2 == 0) { n->is_possible_guard = 1; Loading Loading @@ -1800,14 +1810,14 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg) tt_ptr_op(g2, OP_EQ, g); /* But if we impose a restriction, we don't get the same guard */ entry_guard_restriction_t rst; memset(&rst, 0, sizeof(rst)); memcpy(rst.exclude_id, g->identity, DIGEST_LEN); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); entry_guard_restriction_t *rst; rst = guard_create_exit_restriction((uint8_t*)g->identity); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state); tt_ptr_op(g2, OP_NE, g); done: guard_selection_free(gs); entry_guard_restriction_free(rst); } static void Loading @@ -1817,6 +1827,7 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) guards, we use a confirmed guard. */ (void)arg; int i; entry_guard_restriction_t *rst = NULL; guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); const int N_CONFIRMED = 10; Loading Loading @@ -1877,10 +1888,8 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) get_options_mutable()->EnforceDistinctSubnets = 0; g = smartlist_get(gs->confirmed_entry_guards, smartlist_len(gs->primary_entry_guards)+2); entry_guard_restriction_t rst; memset(&rst, 0, sizeof(rst)); memcpy(rst.exclude_id, g->identity, DIGEST_LEN); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); rst = guard_create_exit_restriction((uint8_t*)g->identity); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state); tt_ptr_op(g2, OP_NE, NULL); tt_ptr_op(g2, OP_NE, g); tt_int_op(g2->confirmed_idx, OP_EQ, Loading @@ -1906,13 +1915,13 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) // Regression test for bug 22753/TROVE-2017-006. get_options_mutable()->EnforceDistinctSubnets = 1; g = smartlist_get(gs->confirmed_entry_guards, 0); memset(&rst, 0, sizeof(rst)); memcpy(rst.exclude_id, g->identity, DIGEST_LEN); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); memcpy(rst->exclude_id, g->identity, DIGEST_LEN); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state); tt_ptr_op(g2, OP_EQ, NULL); done: guard_selection_free(gs); entry_guard_restriction_free(rst); } static void Loading Loading @@ -2493,9 +2502,7 @@ test_entry_guard_upgrade_not_blocked_by_restricted_circ_complete(void *arg) /* Once more, let circ1 become complete. But this time, we'll claim * that circ2 was restricted to not use the same guard as circ1. */ data->guard2_state->restrictions = tor_malloc_zero(sizeof(entry_guard_restriction_t)); memcpy(data->guard2_state->restrictions->exclude_id, data->guard1->identity, DIGEST_LEN); guard_create_exit_restriction((uint8_t*)data->guard1->identity); smartlist_t *result = smartlist_new(); int r; Loading Loading @@ -2604,9 +2611,7 @@ test_entry_guard_upgrade_not_blocked_by_restricted_circ_pending(void *arg) } data->guard2_state->restrictions = tor_malloc_zero(sizeof(entry_guard_restriction_t)); memcpy(data->guard2_state->restrictions->exclude_id, data->guard1->identity, DIGEST_LEN); guard_create_exit_restriction((uint8_t*)data->guard1->identity); smartlist_t *result = smartlist_new(); int r; Loading Loading @@ -2674,6 +2679,112 @@ test_enty_guard_should_expire_waiting(void *arg) tor_free(fake_state); } static void mock_directory_initiate_request(directory_request_t *req) { if (req->guard_state) { circuit_guard_state_free(req->guard_state); } } static networkstatus_t *mock_ns_val = NULL; static networkstatus_t * mock_ns_get_by_flavor(consensus_flavor_t f) { (void)f; return mock_ns_val; } /** Test that when we fetch microdescriptors we skip guards that have * previously failed to serve us needed microdescriptors. */ static void test_entry_guard_outdated_dirserver_exclusion(void *arg) { int retval; response_handler_args_t *args = NULL; dir_connection_t *conn = NULL; (void) arg; /* Test prep: Make a new guard selection */ guard_selection_t *gs = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 1); /* ... we want to use entry guards */ or_options_t *options = get_options_mutable(); options->UseEntryGuards = 1; options->UseBridges = 0; /* ... prepare some md digests we want to download in the future */ smartlist_t *digests = smartlist_new(); const char *prose = "unhurried and wise, we perceive."; for (int i = 0; i < 20; i++) { smartlist_add(digests, (char*)prose); } /* ... now mock some functions */ mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor); MOCK(directory_initiate_request, mock_directory_initiate_request); /* Test logic: * 0. Create a proper guard set and primary guard list. * 1. Pretend to fail microdescriptor fetches from all the primary guards. * 2. Order another microdescriptor fetch and make sure that primary guards * get skipped since they failed previous fetches. */ { /* Setup primary guard list */ int i; entry_guards_update_primary(gs); for (i = 0; i < DFLT_N_PRIMARY_GUARDS; ++i) { entry_guard_t *guard = smartlist_get(gs->sampled_entry_guards, i); make_guard_confirmed(gs, guard); } entry_guards_update_primary(gs); } { /* Fail microdesc fetches with all the primary guards */ args = tor_malloc_zero(sizeof(response_handler_args_t)); args->status_code = 404; args->reason = NULL; args->body = NULL; args->body_len = 0; conn = tor_malloc_zero(sizeof(dir_connection_t)); conn->requested_resource = tor_strdup("d/jlinblackorigami"); conn->base_.purpose = DIR_PURPOSE_FETCH_MICRODESC; /* Pretend to fail fetches with all primary guards */ SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards,const entry_guard_t *,g) { memcpy(conn->identity_digest, g->identity, DIGEST_LEN); retval = handle_response_fetch_microdesc(conn, args); tt_int_op(retval, OP_EQ, 0); } SMARTLIST_FOREACH_END(g); } { /* Now order the final md download */ setup_full_capture_of_logs(LOG_INFO); initiate_descriptor_downloads(NULL, DIR_PURPOSE_FETCH_MICRODESC, digests, 3, 7, 0); /* ... and check that because we failed to fetch microdescs from all our * primaries, we didnt end up selecting a primary for fetching dir info */ expect_log_msg_containing("No primary or confirmed guards available."); teardown_capture_of_logs(); } done: smartlist_free(digests); tor_free(args); if (conn) { tor_free(conn->requested_resource); tor_free(conn); } } static const struct testcase_setup_t big_fake_network = { big_fake_network_setup, big_fake_network_cleanup }; Loading Loading @@ -2734,6 +2845,7 @@ struct testcase_t entrynodes_tests[] = { BFN_TEST(select_for_circuit_highlevel_primary_retry), BFN_TEST(select_and_cancel), BFN_TEST(drop_guards), BFN_TEST(outdated_dirserver_exclusion), UPGRADE_TEST(upgrade_a_circuit, "c1-done c2-done"), UPGRADE_TEST(upgrade_blocked_by_live_primary_guards, "c1-done c2-done"), Loading Loading
src/or/directory.c +0 −42 Original line number Diff line number Diff line Loading @@ -1012,48 +1012,6 @@ directory_must_use_begindir(const or_options_t *options) return !public_server_mode(options); } struct directory_request_t { /** * These fields specify which directory we're contacting. Routerstatus, * if present, overrides the other fields. * * @{ */ tor_addr_port_t or_addr_port; tor_addr_port_t dir_addr_port; char digest[DIGEST_LEN]; const routerstatus_t *routerstatus; /** @} */ /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what * kind of operation we'll be doing (upload/download), and of what kind * of document. */ uint8_t dir_purpose; /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo * and extrainfo docs. */ uint8_t router_purpose; /** Enum: determines whether to anonymize, and whether to use dirport or * orport. */ dir_indirection_t indirection; /** Alias to the variable part of the URL for this request */ const char *resource; /** Alias to the payload to upload (if any) */ const char *payload; /** Number of bytes to upload from payload</b> */ size_t payload_len; /** Value to send in an if-modified-since header, or 0 for none. */ time_t if_modified_since; /** Hidden-service-specific information v2. */ const rend_data_t *rend_query; /** Extra headers to append to the request */ config_line_t *additional_headers; /** Hidden-service-specific information for v3+. */ const hs_ident_dir_conn_t *hs_ident; /** Used internally to directory.c: gets informed when the attempt to * connect to the directory succeeds or fails, if that attempt bears on the * directory's usability as a directory guard. */ circuit_guard_state_t *guard_state; }; /** Evaluate the situation and decide if we should use an encrypted * "begindir-style" connection for this directory request. * 0) If there is no DirPort, yes. Loading
src/or/directory.h +42 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,48 @@ typedef struct response_handler_args_t { const char *headers; } response_handler_args_t; struct directory_request_t { /** * These fields specify which directory we're contacting. Routerstatus, * if present, overrides the other fields. * * @{ */ tor_addr_port_t or_addr_port; tor_addr_port_t dir_addr_port; char digest[DIGEST_LEN]; const routerstatus_t *routerstatus; /** @} */ /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what * kind of operation we'll be doing (upload/download), and of what kind * of document. */ uint8_t dir_purpose; /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo * and extrainfo docs. */ uint8_t router_purpose; /** Enum: determines whether to anonymize, and whether to use dirport or * orport. */ dir_indirection_t indirection; /** Alias to the variable part of the URL for this request */ const char *resource; /** Alias to the payload to upload (if any) */ const char *payload; /** Number of bytes to upload from payload</b> */ size_t payload_len; /** Value to send in an if-modified-since header, or 0 for none. */ time_t if_modified_since; /** Hidden-service-specific information v2. */ const rend_data_t *rend_query; /** Extra headers to append to the request */ config_line_t *additional_headers; /** Hidden-service-specific information for v3+. */ const hs_ident_dir_conn_t *hs_ident; /** Used internally to directory.c: gets informed when the attempt to * connect to the directory succeeds or fails, if that attempt bears on the * directory's usability as a directory guard. */ struct circuit_guard_state_t *guard_state; }; struct get_handler_args_t; STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn, const struct get_handler_args_t *args); Loading
src/test/test_entrynodes.c +129 −17 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #define STATEFILE_PRIVATE #define ENTRYNODES_PRIVATE #define ROUTERLIST_PRIVATE #define DIRECTORY_PRIVATE #include "or.h" #include "test.h" Loading @@ -15,6 +16,7 @@ #include "circuitlist.h" #include "config.h" #include "confparse.h" #include "directory.h" #include "entrynodes.h" #include "nodelist.h" #include "networkstatus.h" Loading Loading @@ -129,6 +131,14 @@ big_fake_network_setup(const struct testcase_t *testcase) n->rs->has_bandwidth = 1; n->rs->bandwidth_kb = 30; /* Make a random nickname for each node */ { char nickname_binary[8]; crypto_rand(nickname_binary, sizeof(nickname_binary)); base64_encode(n->rs->nickname, sizeof(n->rs->nickname), nickname_binary, sizeof(nickname_binary), 0); } /* Call half of the nodes a possible guard. */ if (i % 2 == 0) { n->is_possible_guard = 1; Loading Loading @@ -1800,14 +1810,14 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg) tt_ptr_op(g2, OP_EQ, g); /* But if we impose a restriction, we don't get the same guard */ entry_guard_restriction_t rst; memset(&rst, 0, sizeof(rst)); memcpy(rst.exclude_id, g->identity, DIGEST_LEN); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); entry_guard_restriction_t *rst; rst = guard_create_exit_restriction((uint8_t*)g->identity); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state); tt_ptr_op(g2, OP_NE, g); done: guard_selection_free(gs); entry_guard_restriction_free(rst); } static void Loading @@ -1817,6 +1827,7 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) guards, we use a confirmed guard. */ (void)arg; int i; entry_guard_restriction_t *rst = NULL; guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); const int N_CONFIRMED = 10; Loading Loading @@ -1877,10 +1888,8 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) get_options_mutable()->EnforceDistinctSubnets = 0; g = smartlist_get(gs->confirmed_entry_guards, smartlist_len(gs->primary_entry_guards)+2); entry_guard_restriction_t rst; memset(&rst, 0, sizeof(rst)); memcpy(rst.exclude_id, g->identity, DIGEST_LEN); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); rst = guard_create_exit_restriction((uint8_t*)g->identity); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state); tt_ptr_op(g2, OP_NE, NULL); tt_ptr_op(g2, OP_NE, g); tt_int_op(g2->confirmed_idx, OP_EQ, Loading @@ -1906,13 +1915,13 @@ test_entry_guard_select_for_circuit_confirmed(void *arg) // Regression test for bug 22753/TROVE-2017-006. get_options_mutable()->EnforceDistinctSubnets = 1; g = smartlist_get(gs->confirmed_entry_guards, 0); memset(&rst, 0, sizeof(rst)); memcpy(rst.exclude_id, g->identity, DIGEST_LEN); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); memcpy(rst->exclude_id, g->identity, DIGEST_LEN); g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state); tt_ptr_op(g2, OP_EQ, NULL); done: guard_selection_free(gs); entry_guard_restriction_free(rst); } static void Loading Loading @@ -2493,9 +2502,7 @@ test_entry_guard_upgrade_not_blocked_by_restricted_circ_complete(void *arg) /* Once more, let circ1 become complete. But this time, we'll claim * that circ2 was restricted to not use the same guard as circ1. */ data->guard2_state->restrictions = tor_malloc_zero(sizeof(entry_guard_restriction_t)); memcpy(data->guard2_state->restrictions->exclude_id, data->guard1->identity, DIGEST_LEN); guard_create_exit_restriction((uint8_t*)data->guard1->identity); smartlist_t *result = smartlist_new(); int r; Loading Loading @@ -2604,9 +2611,7 @@ test_entry_guard_upgrade_not_blocked_by_restricted_circ_pending(void *arg) } data->guard2_state->restrictions = tor_malloc_zero(sizeof(entry_guard_restriction_t)); memcpy(data->guard2_state->restrictions->exclude_id, data->guard1->identity, DIGEST_LEN); guard_create_exit_restriction((uint8_t*)data->guard1->identity); smartlist_t *result = smartlist_new(); int r; Loading Loading @@ -2674,6 +2679,112 @@ test_enty_guard_should_expire_waiting(void *arg) tor_free(fake_state); } static void mock_directory_initiate_request(directory_request_t *req) { if (req->guard_state) { circuit_guard_state_free(req->guard_state); } } static networkstatus_t *mock_ns_val = NULL; static networkstatus_t * mock_ns_get_by_flavor(consensus_flavor_t f) { (void)f; return mock_ns_val; } /** Test that when we fetch microdescriptors we skip guards that have * previously failed to serve us needed microdescriptors. */ static void test_entry_guard_outdated_dirserver_exclusion(void *arg) { int retval; response_handler_args_t *args = NULL; dir_connection_t *conn = NULL; (void) arg; /* Test prep: Make a new guard selection */ guard_selection_t *gs = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 1); /* ... we want to use entry guards */ or_options_t *options = get_options_mutable(); options->UseEntryGuards = 1; options->UseBridges = 0; /* ... prepare some md digests we want to download in the future */ smartlist_t *digests = smartlist_new(); const char *prose = "unhurried and wise, we perceive."; for (int i = 0; i < 20; i++) { smartlist_add(digests, (char*)prose); } /* ... now mock some functions */ mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor); MOCK(directory_initiate_request, mock_directory_initiate_request); /* Test logic: * 0. Create a proper guard set and primary guard list. * 1. Pretend to fail microdescriptor fetches from all the primary guards. * 2. Order another microdescriptor fetch and make sure that primary guards * get skipped since they failed previous fetches. */ { /* Setup primary guard list */ int i; entry_guards_update_primary(gs); for (i = 0; i < DFLT_N_PRIMARY_GUARDS; ++i) { entry_guard_t *guard = smartlist_get(gs->sampled_entry_guards, i); make_guard_confirmed(gs, guard); } entry_guards_update_primary(gs); } { /* Fail microdesc fetches with all the primary guards */ args = tor_malloc_zero(sizeof(response_handler_args_t)); args->status_code = 404; args->reason = NULL; args->body = NULL; args->body_len = 0; conn = tor_malloc_zero(sizeof(dir_connection_t)); conn->requested_resource = tor_strdup("d/jlinblackorigami"); conn->base_.purpose = DIR_PURPOSE_FETCH_MICRODESC; /* Pretend to fail fetches with all primary guards */ SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards,const entry_guard_t *,g) { memcpy(conn->identity_digest, g->identity, DIGEST_LEN); retval = handle_response_fetch_microdesc(conn, args); tt_int_op(retval, OP_EQ, 0); } SMARTLIST_FOREACH_END(g); } { /* Now order the final md download */ setup_full_capture_of_logs(LOG_INFO); initiate_descriptor_downloads(NULL, DIR_PURPOSE_FETCH_MICRODESC, digests, 3, 7, 0); /* ... and check that because we failed to fetch microdescs from all our * primaries, we didnt end up selecting a primary for fetching dir info */ expect_log_msg_containing("No primary or confirmed guards available."); teardown_capture_of_logs(); } done: smartlist_free(digests); tor_free(args); if (conn) { tor_free(conn->requested_resource); tor_free(conn); } } static const struct testcase_setup_t big_fake_network = { big_fake_network_setup, big_fake_network_cleanup }; Loading Loading @@ -2734,6 +2845,7 @@ struct testcase_t entrynodes_tests[] = { BFN_TEST(select_for_circuit_highlevel_primary_retry), BFN_TEST(select_and_cancel), BFN_TEST(drop_guards), BFN_TEST(outdated_dirserver_exclusion), UPGRADE_TEST(upgrade_a_circuit, "c1-done c2-done"), UPGRADE_TEST(upgrade_blocked_by_live_primary_guards, "c1-done c2-done"), Loading