Move voteflags.[ch] to become dirauth only.

For various reasons, this was a nontrivial movement.  There are
several places in the code where we do something like "update the
flags on this routerstatus or node if we're an authority", and at
least one where we pretended to be an authority when we weren't.
This commit is contained in:
Nick Mathewson 2019-04-30 12:42:33 -04:00
parent 996f7c75ba
commit 6f42efaa59
12 changed files with 173 additions and 120 deletions

View File

@ -153,8 +153,8 @@ LIBTOR_APP_A_SOURCES = \
# These should eventually move into module_dirauth_sources, but for now # These should eventually move into module_dirauth_sources, but for now
# the separation is only in the code location. # the separation is only in the code location.
LIBTOR_APP_A_SOURCES += \ LIBTOR_APP_A_SOURCES += \
src/feature/dirauth/bwauth.c \ src/feature/dirauth/bwauth.c
src/feature/dirauth/voteflags.c
if BUILD_NT_SERVICES if BUILD_NT_SERVICES
LIBTOR_APP_A_SOURCES += src/app/main/ntmain.c LIBTOR_APP_A_SOURCES += src/app/main/ntmain.c
@ -171,6 +171,7 @@ LIBTOR_APP_TESTING_A_SOURCES = $(LIBTOR_APP_A_SOURCES)
# The Directory Authority module. # The Directory Authority module.
MODULE_DIRAUTH_SOURCES = \ MODULE_DIRAUTH_SOURCES = \
src/feature/dirauth/authmode.c \ src/feature/dirauth/authmode.c \
src/feature/dirauth/bridgeauth.c \
src/feature/dirauth/dirauth_periodic.c \ src/feature/dirauth/dirauth_periodic.c \
src/feature/dirauth/dirauth_sys.c \ src/feature/dirauth/dirauth_sys.c \
src/feature/dirauth/dircollate.c \ src/feature/dirauth/dircollate.c \
@ -181,7 +182,8 @@ MODULE_DIRAUTH_SOURCES = \
src/feature/dirauth/reachability.c \ src/feature/dirauth/reachability.c \
src/feature/dirauth/recommend_pkg.c \ src/feature/dirauth/recommend_pkg.c \
src/feature/dirauth/shared_random.c \ src/feature/dirauth/shared_random.c \
src/feature/dirauth/shared_random_state.c src/feature/dirauth/shared_random_state.c \
src/feature/dirauth/voteflags.c
if BUILD_MODULE_DIRAUTH if BUILD_MODULE_DIRAUTH
LIBTOR_APP_A_SOURCES += $(MODULE_DIRAUTH_SOURCES) LIBTOR_APP_A_SOURCES += $(MODULE_DIRAUTH_SOURCES)
@ -310,6 +312,7 @@ noinst_HEADERS += \
src/feature/control/fmt_serverstatus.h \ src/feature/control/fmt_serverstatus.h \
src/feature/control/getinfo_geoip.h \ src/feature/control/getinfo_geoip.h \
src/feature/dirauth/authmode.h \ src/feature/dirauth/authmode.h \
src/feature/dirauth/bridgeauth.h \
src/feature/dirauth/bwauth.h \ src/feature/dirauth/bwauth.h \
src/feature/dirauth/dirauth_periodic.h \ src/feature/dirauth/dirauth_periodic.h \
src/feature/dirauth/dirauth_sys.h \ src/feature/dirauth/dirauth_sys.h \

View File

@ -75,6 +75,7 @@
#include "feature/control/control.h" #include "feature/control/control.h"
#include "feature/control/control_events.h" #include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h" #include "feature/dirauth/authmode.h"
#include "feature/dirauth/bridgeauth.h"
#include "feature/dircache/consdiffmgr.h" #include "feature/dircache/consdiffmgr.h"
#include "feature/dircache/dirserv.h" #include "feature/dircache/dirserv.h"
#include "feature/dircommon/directory.h" #include "feature/dircommon/directory.h"
@ -1366,7 +1367,6 @@ CALLBACK(retry_listeners);
CALLBACK(rotate_onion_key); CALLBACK(rotate_onion_key);
CALLBACK(rotate_x509_certificate); CALLBACK(rotate_x509_certificate);
CALLBACK(save_state); CALLBACK(save_state);
CALLBACK(write_bridge_ns);
CALLBACK(write_stats_file); CALLBACK(write_stats_file);
CALLBACK(control_per_second_events); CALLBACK(control_per_second_events);
CALLBACK(second_elapsed); CALLBACK(second_elapsed);
@ -1433,9 +1433,6 @@ STATIC periodic_event_item_t mainloop_periodic_events[] = {
/* XXXX this could be restricted to CLIENT+NET_PARTICIPANT */ /* XXXX this could be restricted to CLIENT+NET_PARTICIPANT */
CALLBACK(rend_cache_failure_clean, NET_PARTICIPANT, FL(RUN_ON_DISABLE)), CALLBACK(rend_cache_failure_clean, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
/* Bridge Authority only. */
CALLBACK(write_bridge_ns, BRIDGEAUTH, 0),
/* Directory server only. */ /* Directory server only. */
CALLBACK(clean_consdiffmgr, DIRSERVER, 0), CALLBACK(clean_consdiffmgr, DIRSERVER, 0),
@ -2369,22 +2366,6 @@ check_dns_honesty_callback(time_t now, const or_options_t *options)
return 12*3600 + crypto_rand_int(12*3600); return 12*3600 + crypto_rand_int(12*3600);
} }
/**
* Periodic callback: if we're the bridge authority, write a networkstatus
* file to disk.
*/
static int
write_bridge_ns_callback(time_t now, const or_options_t *options)
{
/* 10. write bridge networkstatus file to disk */
if (options->BridgeAuthoritativeDir) {
networkstatus_dump_bridge_status_to_file(now);
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
return BRIDGE_STATUSFILE_INTERVAL;
}
return PERIODIC_EVENT_NO_UPDATE;
}
static int heartbeat_callback_first_time = 1; static int heartbeat_callback_first_time = 1;
/** /**

View File

@ -76,7 +76,6 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
const node_t *node = node_get_by_id(ri->cache_info.identity_digest); const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
tor_assert(node); tor_assert(node);
if (for_controller) { if (for_controller) {
char name_buf[MAX_VERBOSE_NICKNAME_LEN+2]; char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
char *cp = name_buf; char *cp = name_buf;

View File

@ -0,0 +1,55 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "core/or/or.h"
#include "feature/dirauth/bridgeauth.h"
#include "feature/dirauth/voteflags.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/relay/router.h"
#include "app/config/config.h"
#include "feature/nodelist/routerinfo_st.h"
/** Write out router status entries for all our bridge descriptors. Here, we
* also mark routers as running. */
void
bridgeauth_dump_bridge_status_to_file(time_t now)
{
char *status;
char *fname = NULL;
char *thresholds = NULL;
char *published_thresholds_and_status = NULL;
char published[ISO_TIME_LEN+1];
const routerinfo_t *me = router_get_my_routerinfo();
char fingerprint[FINGERPRINT_LEN+1];
char *fingerprint_line = NULL;
dirserv_set_bridges_running(now);
status = networkstatus_getinfo_by_purpose("bridge", now);
if (me && crypto_pk_get_fingerprint(me->identity_pkey,
fingerprint, 0) >= 0) {
tor_asprintf(&fingerprint_line, "fingerprint %s\n", fingerprint);
} else {
log_warn(LD_BUG, "Error computing fingerprint for bridge status.");
}
format_iso_time(published, now);
dirserv_compute_bridge_flag_thresholds();
thresholds = dirserv_get_flag_thresholds_line();
tor_asprintf(&published_thresholds_and_status,
"published %s\nflag-thresholds %s\n%s%s",
published, thresholds, fingerprint_line ? fingerprint_line : "",
status);
fname = get_datadir_fname("networkstatus-bridges");
if (write_str_to_file(fname,published_thresholds_and_status,0)<0) {
log_warn(LD_DIRSERV, "Unable to write networkstatus-bridges file.");
}
tor_free(thresholds);
tor_free(published_thresholds_and_status);
tor_free(fname);
tor_free(status);
tor_free(fingerprint_line);
}

View File

@ -0,0 +1,12 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_DIRAUTH_BRIDGEAUTH_H
#define TOR_DIRAUTH_BRIDGEAUTH_H
void bridgeauth_dump_bridge_status_to_file(time_t now);
#endif

View File

@ -11,6 +11,7 @@
#include "feature/dirauth/reachability.h" #include "feature/dirauth/reachability.h"
#include "feature/stats/rephist.h" #include "feature/stats/rephist.h"
#include "feature/dirauth/bridgeauth.h"
#include "feature/dirauth/dirvote.h" #include "feature/dirauth/dirvote.h"
#include "feature/dirauth/dirauth_periodic.h" #include "feature/dirauth/dirauth_periodic.h"
#include "feature/dirauth/authmode.h" #include "feature/dirauth/authmode.h"
@ -131,6 +132,23 @@ downrate_stability_callback(time_t now, const or_options_t *options)
DECLARE_EVENT(downrate_stability, AUTHORITIES, 0); DECLARE_EVENT(downrate_stability, AUTHORITIES, 0);
/**
* Periodic callback: if we're the bridge authority, write a networkstatus
* file to disk.
*/
static int
write_bridge_ns_callback(time_t now, const or_options_t *options)
{
if (options->BridgeAuthoritativeDir) {
bridgeauth_dump_bridge_status_to_file(now);
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
return BRIDGE_STATUSFILE_INTERVAL;
}
return PERIODIC_EVENT_NO_UPDATE;
}
DECLARE_EVENT(write_bridge_ns, BRIDGEAUTH, 0);
void void
dirauth_register_periodic_events(void) dirauth_register_periodic_events(void)
{ {
@ -139,4 +157,5 @@ dirauth_register_periodic_events(void)
periodic_events_register(&save_stability_event); periodic_events_register(&save_stability_event);
periodic_events_register(&check_authority_cert_event); periodic_events_register(&check_authority_cert_event);
periodic_events_register(&dirvote_event); periodic_events_register(&dirvote_event);
periodic_events_register(&write_bridge_ns_event);
} }

View File

@ -4545,8 +4545,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status; rs = &vrs->status;
set_routerstatus_from_routerinfo(rs, node, ri, now, dirauth_set_routerstatus_from_routerinfo(rs, node, ri, now,
listbadexits); listbadexits);
if (ri->cache_info.signing_key_cert) { if (ri->cache_info.signing_key_cert) {
memcpy(vrs->ed25519_id, memcpy(vrs->ed25519_id,

View File

@ -546,38 +546,31 @@ should_publish_node_ipv6(const node_t *node, const routerinfo_t *ri,
router_is_me(ri)); router_is_me(ri));
} }
/** Extract status information from <b>ri</b> and from other authority /**
* functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is * Extract status information from <b>ri</b> and from other authority
* set. * functions and store it in <b>rs</b>, as per
* * <b>set_routerstatus_from_routerinfo</b>. Additionally, sets information
* We assume that ri-\>is_running has already been set, e.g. by * in from the authority subsystem.
* dirserv_set_router_is_running(ri, now);
*/ */
void void
set_routerstatus_from_routerinfo(routerstatus_t *rs, dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs,
node_t *node, node_t *node,
const routerinfo_t *ri, const routerinfo_t *ri,
time_t now, time_t now,
int listbadexits) int listbadexits)
{ {
const or_options_t *options = get_options(); const or_options_t *options = get_options();
uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri); uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri);
memset(rs, 0, sizeof(routerstatus_t)); /* Set these flags so that set_routerstatus_from_routerinfo can copy them.
*/
node->is_stable = !dirserv_thinks_router_is_unreliable(now, ri, 1, 0);
node->is_fast = !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now);
rs->is_authority = set_routerstatus_from_routerinfo(rs, node, ri);
router_digest_is_trusted_dir(ri->cache_info.identity_digest);
/* Already set by compute_performance_thresholds. */
rs->is_exit = node->is_exit;
rs->is_stable = node->is_stable =
!dirserv_thinks_router_is_unreliable(now, ri, 1, 0);
rs->is_fast = node->is_fast =
!dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
rs->is_flagged_running = node->is_running; /* computed above */
rs->is_valid = node->is_valid;
/* Override rs->is_possible_guard. */
if (node->is_fast && node->is_stable && if (node->is_fast && node->is_stable &&
ri->supports_tunnelled_dir_requests && ri->supports_tunnelled_dir_requests &&
((options->AuthDirGuardBWGuarantee && ((options->AuthDirGuardBWGuarantee &&
@ -593,31 +586,16 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
rs->is_possible_guard = 0; rs->is_possible_guard = 0;
} }
/* Override rs->is_bad_exit */
rs->is_bad_exit = listbadexits && node->is_bad_exit; rs->is_bad_exit = listbadexits && node->is_bad_exit;
rs->is_hs_dir = node->is_hs_dir =
dirserv_thinks_router_is_hs_dir(ri, node, now);
rs->is_named = rs->is_unnamed = 0;
rs->published_on = ri->cache_info.published_on;
memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
DIGEST_LEN);
rs->addr = ri->addr;
strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
rs->or_port = ri->or_port;
rs->dir_port = ri->dir_port;
rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
/* Set rs->is_staledesc. */
rs->is_staledesc = rs->is_staledesc =
(ri->cache_info.published_on + DESC_IS_STALE_INTERVAL) < now; (ri->cache_info.published_on + DESC_IS_STALE_INTERVAL) < now;
if (should_publish_node_ipv6(node, ri, now)) { if (! should_publish_node_ipv6(node, ri, now)) {
/* We're configured as having IPv6 connectivity. There's an IPv6 /* We're not configured as having IPv6 connectivity or the node isn't:
OR port and it's reachable so copy it to the routerstatus. */ * zero its IPv6 information. */
tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr);
rs->ipv6_orport = ri->ipv6_orport;
} else {
tor_addr_make_null(&rs->ipv6_addr, AF_INET6); tor_addr_make_null(&rs->ipv6_addr, AF_INET6);
rs->ipv6_orport = 0; rs->ipv6_orport = 0;
} }

View File

@ -12,18 +12,20 @@
#ifndef TOR_VOTEFLAGS_H #ifndef TOR_VOTEFLAGS_H
#define TOR_VOTEFLAGS_H #define TOR_VOTEFLAGS_H
#ifdef HAVE_MODULE_DIRAUTH
void dirserv_set_router_is_running(routerinfo_t *router, time_t now); void dirserv_set_router_is_running(routerinfo_t *router, time_t now);
char *dirserv_get_flag_thresholds_line(void); char *dirserv_get_flag_thresholds_line(void);
void dirserv_compute_bridge_flag_thresholds(void); void dirserv_compute_bridge_flag_thresholds(void);
int running_long_enough_to_decide_unreachable(void); int running_long_enough_to_decide_unreachable(void);
void set_routerstatus_from_routerinfo(routerstatus_t *rs, void dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs,
node_t *node, node_t *node,
const routerinfo_t *ri, const routerinfo_t *ri,
time_t now, time_t now,
int listbadexits); int listbadexits);
void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil); void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil);
#endif
void dirserv_set_bridges_running(time_t now); void dirserv_set_bridges_running(time_t now);

View File

@ -2366,6 +2366,49 @@ networkstatus_getinfo_helper_single(const routerstatus_t *rs)
NULL); NULL);
} }
/**
* Extract status information from <b>ri</b> and from other authority
* functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is
* set.
*
* We assume that node-\>is_running has already been set, e.g. by
* dirserv_set_router_is_running(ri, now);
*/
void
set_routerstatus_from_routerinfo(routerstatus_t *rs,
const node_t *node,
const routerinfo_t *ri)
{
memset(rs, 0, sizeof(routerstatus_t));
rs->is_authority =
router_digest_is_trusted_dir(ri->cache_info.identity_digest);
/* Set by compute_performance_thresholds or from consensus */
rs->is_exit = node->is_exit;
rs->is_stable = node->is_stable;
rs->is_fast = node->is_fast;
rs->is_flagged_running = node->is_running;
rs->is_valid = node->is_valid;
rs->is_possible_guard = node->is_possible_guard;
rs->is_bad_exit = node->is_bad_exit;
rs->is_hs_dir = node->is_hs_dir;
rs->is_named = rs->is_unnamed = 0;
rs->published_on = ri->cache_info.published_on;
memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
DIGEST_LEN);
rs->addr = ri->addr;
strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
rs->or_port = ri->or_port;
rs->dir_port = ri->dir_port;
rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr);
rs->ipv6_orport = ri->ipv6_orport;
}
/** Alloc and return a string describing routerstatuses for the most /** Alloc and return a string describing routerstatuses for the most
* recent info of each router we know about that is of purpose * recent info of each router we know about that is of purpose
* <b>purpose_string</b>. Return NULL if unrecognized purpose. * <b>purpose_string</b>. Return NULL if unrecognized purpose.
@ -2398,8 +2441,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
continue; continue;
if (ri->purpose != purpose) if (ri->purpose != purpose)
continue; continue;
/* then generate and write out status lines for each of them */ set_routerstatus_from_routerinfo(&rs, node, ri);
set_routerstatus_from_routerinfo(&rs, node, ri, now, 0);
smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs)); smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs));
} SMARTLIST_FOREACH_END(ri); } SMARTLIST_FOREACH_END(ri);
@ -2409,47 +2451,6 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
return answer; return answer;
} }
/** Write out router status entries for all our bridge descriptors. Here, we
* also mark routers as running. */
void
networkstatus_dump_bridge_status_to_file(time_t now)
{
char *status;
char *fname = NULL;
char *thresholds = NULL;
char *published_thresholds_and_status = NULL;
char published[ISO_TIME_LEN+1];
const routerinfo_t *me = router_get_my_routerinfo();
char fingerprint[FINGERPRINT_LEN+1];
char *fingerprint_line = NULL;
dirserv_set_bridges_running(now);
status = networkstatus_getinfo_by_purpose("bridge", now);
if (me && crypto_pk_get_fingerprint(me->identity_pkey,
fingerprint, 0) >= 0) {
tor_asprintf(&fingerprint_line, "fingerprint %s\n", fingerprint);
} else {
log_warn(LD_BUG, "Error computing fingerprint for bridge status.");
}
format_iso_time(published, now);
dirserv_compute_bridge_flag_thresholds();
thresholds = dirserv_get_flag_thresholds_line();
tor_asprintf(&published_thresholds_and_status,
"published %s\nflag-thresholds %s\n%s%s",
published, thresholds, fingerprint_line ? fingerprint_line : "",
status);
fname = get_datadir_fname("networkstatus-bridges");
if (write_str_to_file(fname,published_thresholds_and_status,0)<0) {
log_warn(LD_DIRSERV, "Unable to write networkstatus-bridges file.");
}
tor_free(thresholds);
tor_free(published_thresholds_and_status);
tor_free(fname);
tor_free(status);
tor_free(fingerprint_line);
}
/* DOCDOC get_net_param_from_list */ /* DOCDOC get_net_param_from_list */
static int32_t static int32_t
get_net_param_from_list(smartlist_t *net_params, const char *param_name, get_net_param_from_list(smartlist_t *net_params, const char *param_name,

View File

@ -122,7 +122,6 @@ void signed_descs_update_status_from_consensus_networkstatus(
char *networkstatus_getinfo_helper_single(const routerstatus_t *rs); char *networkstatus_getinfo_helper_single(const routerstatus_t *rs);
char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now); char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now);
void networkstatus_dump_bridge_status_to_file(time_t now);
MOCK_DECL(int32_t, networkstatus_get_param, MOCK_DECL(int32_t, networkstatus_get_param,
(const networkstatus_t *ns, const char *param_name, (const networkstatus_t *ns, const char *param_name,
int32_t default_val, int32_t min_val, int32_t max_val)); int32_t default_val, int32_t min_val, int32_t max_val));
@ -149,6 +148,10 @@ void vote_routerstatus_free_(vote_routerstatus_t *rs);
#define vote_routerstatus_free(rs) \ #define vote_routerstatus_free(rs) \
FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs)) FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs))
void set_routerstatus_from_routerinfo(routerstatus_t *rs,
const node_t *node,
const routerinfo_t *ri);
#ifdef NETWORKSTATUS_PRIVATE #ifdef NETWORKSTATUS_PRIVATE
#ifdef TOR_UNIT_TESTS #ifdef TOR_UNIT_TESTS
STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c, STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c,

View File

@ -60,7 +60,7 @@ check_result(flag_vote_test_cfg_t *c)
bool result = false; bool result = false;
routerstatus_t rs; routerstatus_t rs;
memset(&rs, 0, sizeof(rs)); memset(&rs, 0, sizeof(rs));
set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now, 0); dirauth_set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now, 0);
tt_i64_op(rs.published_on, OP_EQ, c->expected.published_on); tt_i64_op(rs.published_on, OP_EQ, c->expected.published_on);
tt_str_op(rs.nickname, OP_EQ, c->expected.nickname); tt_str_op(rs.nickname, OP_EQ, c->expected.nickname);