1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
7 * \file fmt_routerstatus.h
8 * \brief Format routerstatus entries for controller, vote, or consensus.
10 * (Because controllers consume this format, we can't make this
14 #include "core/or/or.h"
15 #include "feature/nodelist/fmt_routerstatus.h"
17 #include "core/or/policies.h"
18 #include "feature/dirauth/dirvote.h"
19 #include "feature/nodelist/routerinfo_st.h"
20 #include "feature/nodelist/routerlist.h"
21 #include "feature/nodelist/vote_routerstatus_st.h"
22 #include "feature/stats/rephist.h"
24 #include "lib/crypt_ops/crypto_format.h"
26 /** Helper: write the router-status information in <b>rs</b> into a newly
27 * allocated character buffer. Use the same format as in network-status
28 * documents. If <b>version</b> is non-NULL, add a "v" line for the platform.
30 * Return 0 on success, -1 on failure.
32 * The format argument has one of the following values:
33 * NS_V2 - Output an entry suitable for a V2 NS opinion document
34 * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
35 * for consensus_method.
36 * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc
37 * consensus entry for consensus_method.
38 * NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present,
39 * it contains additional information for the vote.
40 * NS_CONTROL_PORT - Output a NS document for the control port.
43 routerstatus_format_entry(const routerstatus_t
*rs
, const char *version
,
44 const char *protocols
,
45 routerstatus_format_type_t format
,
46 const vote_routerstatus_t
*vrs
)
51 char published
[ISO_TIME_LEN
+1];
52 char identity64
[BASE64_DIGEST_LEN
+1];
53 char digest64
[BASE64_DIGEST_LEN
+1];
54 smartlist_t
*chunks
= smartlist_new();
56 const char *ip_str
= fmt_addr(&rs
->ipv4_addr
);
57 if (ip_str
[0] == '\0')
60 format_iso_time(published
, rs
->published_on
);
61 digest_to_base64(identity64
, rs
->identity_digest
);
62 digest_to_base64(digest64
, rs
->descriptor_digest
);
64 smartlist_add_asprintf(chunks
,
65 "r %s %s %s%s%s %s %" PRIu16
" %" PRIu16
"\n",
68 (format
==NS_V3_CONSENSUS_MICRODESC
)?"":digest64
,
69 (format
==NS_V3_CONSENSUS_MICRODESC
)?"":" ",
75 /* TODO: Maybe we want to pass in what we need to build the rest of
76 * this here, instead of in the caller. Then we could use the
77 * networkstatus_type_t values, with an additional control port value
80 /* Possible "a" line. At most one for now. */
81 if (!tor_addr_is_null(&rs
->ipv6_addr
)) {
82 smartlist_add_asprintf(chunks
, "a %s\n",
83 fmt_addrport(&rs
->ipv6_addr
, rs
->ipv6_orport
));
86 if (format
== NS_V3_CONSENSUS
|| format
== NS_V3_CONSENSUS_MICRODESC
)
89 smartlist_add_asprintf(chunks
,
90 "s%s%s%s%s%s%s%s%s%s%s%s%s\n",
91 /* These must stay in alphabetical order. */
92 rs
->is_authority
?" Authority":"",
93 rs
->is_bad_exit
?" BadExit":"",
94 rs
->is_exit
?" Exit":"",
95 rs
->is_fast
?" Fast":"",
96 rs
->is_possible_guard
?" Guard":"",
97 rs
->is_hs_dir
?" HSDir":"",
98 rs
->is_flagged_running
?" Running":"",
99 rs
->is_stable
?" Stable":"",
100 rs
->is_staledesc
?" StaleDesc":"",
101 rs
->is_sybil
?" Sybil":"",
102 rs
->is_v2_dir
?" V2Dir":"",
103 rs
->is_valid
?" Valid":"");
105 /* length of "opt v \n" */
106 #define V_LINE_OVERHEAD 7
107 if (version
&& strlen(version
) < MAX_V_LINE_LEN
- V_LINE_OVERHEAD
) {
108 smartlist_add_asprintf(chunks
, "v %s\n", version
);
111 smartlist_add_asprintf(chunks
, "pr %s\n", protocols
);
114 if (format
!= NS_V2
) {
115 const routerinfo_t
* desc
= router_get_by_id_digest(rs
->identity_digest
);
118 if (format
!= NS_CONTROL_PORT
) {
119 /* Blow up more or less nicely if we didn't get anything or not the
121 * This should be kept in sync with the function
122 * routerstatus_has_visibly_changed and the struct routerstatus_t
125 char id
[HEX_DIGEST_LEN
+1];
126 char dd
[HEX_DIGEST_LEN
+1];
128 base16_encode(id
, sizeof(id
), rs
->identity_digest
, DIGEST_LEN
);
129 base16_encode(dd
, sizeof(dd
), rs
->descriptor_digest
, DIGEST_LEN
);
130 log_warn(LD_BUG
, "Cannot get any descriptor for %s "
131 "(wanted descriptor %s).",
136 /* This assert could fire for the control port, because
137 * it can request NS documents before all descriptors
138 * have been fetched. Therefore, we only do this test when
139 * format != NS_CONTROL_PORT. */
140 if (tor_memneq(desc
->cache_info
.signed_descriptor_digest
,
141 rs
->descriptor_digest
,
143 char rl_d
[HEX_DIGEST_LEN
+1];
144 char rs_d
[HEX_DIGEST_LEN
+1];
145 char id
[HEX_DIGEST_LEN
+1];
147 base16_encode(rl_d
, sizeof(rl_d
),
148 desc
->cache_info
.signed_descriptor_digest
, DIGEST_LEN
);
149 base16_encode(rs_d
, sizeof(rs_d
), rs
->descriptor_digest
, DIGEST_LEN
);
150 base16_encode(id
, sizeof(id
), rs
->identity_digest
, DIGEST_LEN
);
151 log_err(LD_BUG
, "descriptor digest in routerlist does not match "
152 "the one in routerstatus: %s vs %s "
156 tor_assert(tor_memeq(desc
->cache_info
.signed_descriptor_digest
,
157 rs
->descriptor_digest
,
162 if (format
== NS_CONTROL_PORT
&& rs
->has_bandwidth
) {
163 bw_kb
= rs
->bandwidth_kb
;
166 bw_kb
= router_get_advertised_bandwidth_capped(desc
) / 1000;
168 smartlist_add_asprintf(chunks
,
169 "w Bandwidth=%d", bw_kb
);
171 if (format
== NS_V3_VOTE
&& vrs
&& vrs
->has_measured_bw
) {
172 smartlist_add_asprintf(chunks
,
173 " Measured=%d", vrs
->measured_bw_kb
);
175 /* Write down guardfraction information if we have it. */
176 if (format
== NS_V3_VOTE
&& vrs
&& vrs
->status
.has_guardfraction
) {
177 smartlist_add_asprintf(chunks
,
179 vrs
->status
.guardfraction_percentage
);
182 smartlist_add_strdup(chunks
, "\n");
185 summary
= policy_summarize(desc
->exit_policy
, AF_INET
);
186 smartlist_add_asprintf(chunks
, "p %s\n", summary
);
190 if (format
== NS_V3_VOTE
&& vrs
) {
191 if (fast_mem_is_zero((char*)vrs
->ed25519_id
, ED25519_PUBKEY_LEN
)) {
192 smartlist_add_strdup(chunks
, "id ed25519 none\n");
194 char ed_b64
[BASE64_DIGEST256_LEN
+1];
195 digest256_to_base64(ed_b64
, (const char*)vrs
->ed25519_id
);
196 smartlist_add_asprintf(chunks
, "id ed25519 %s\n", ed_b64
);
199 /* We'll add a series of statistics to the vote per relays so we are
200 * able to assess what each authorities sees and help our health and
201 * performance work. */
202 time_t now
= time(NULL
);
203 smartlist_add_asprintf(chunks
, "stats wfu=%.6f tk=%lu mtbf=%.0f\n",
204 rep_hist_get_weighted_fractional_uptime(rs
->identity_digest
, now
),
205 rep_hist_get_weighted_time_known(rs
->identity_digest
, now
),
206 rep_hist_get_stability(rs
->identity_digest
, now
));
211 result
= smartlist_join_strings(chunks
, "", 0, NULL
);
214 SMARTLIST_FOREACH(chunks
, char *, cp
, tor_free(cp
));
215 smartlist_free(chunks
);