Line data Source code
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 */
5 :
6 : /**
7 : * \file fmt_routerstatus.h
8 : * \brief Format routerstatus entries for controller, vote, or consensus.
9 : *
10 : * (Because controllers consume this format, we can't make this
11 : * code dirauth-only.)
12 : **/
13 :
14 : #include "core/or/or.h"
15 : #include "feature/nodelist/fmt_routerstatus.h"
16 :
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"
23 :
24 : #include "lib/crypt_ops/crypto_format.h"
25 :
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.
29 : *
30 : * Return 0 on success, -1 on failure.
31 : *
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.
41 : */
42 : char *
43 239 : 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)
47 : {
48 239 : char *summary;
49 239 : char *result = NULL;
50 :
51 239 : char published[ISO_TIME_LEN+1];
52 239 : char identity64[BASE64_DIGEST_LEN+1];
53 239 : char digest64[BASE64_DIGEST_LEN+1];
54 239 : smartlist_t *chunks = smartlist_new();
55 :
56 239 : const char *ip_str = fmt_addr(&rs->ipv4_addr);
57 239 : if (ip_str[0] == '\0')
58 0 : goto err;
59 :
60 239 : format_iso_time(published, rs->published_on);
61 239 : digest_to_base64(identity64, rs->identity_digest);
62 239 : digest_to_base64(digest64, rs->descriptor_digest);
63 :
64 239 : smartlist_add_asprintf(chunks,
65 : "r %s %s %s%s%s %s %" PRIu16 " %" PRIu16 "\n",
66 239 : rs->nickname,
67 : identity64,
68 : (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64,
69 : (format==NS_V3_CONSENSUS_MICRODESC)?"":" ",
70 : published,
71 : ip_str,
72 239 : rs->ipv4_orport,
73 239 : rs->ipv4_dirport);
74 :
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
78 : * added -MP */
79 :
80 : /* Possible "a" line. At most one for now. */
81 239 : if (!tor_addr_is_null(&rs->ipv6_addr)) {
82 88 : smartlist_add_asprintf(chunks, "a %s\n",
83 88 : fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
84 : }
85 :
86 239 : if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC)
87 78 : goto done;
88 :
89 161 : 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 161 : rs->is_authority?" Authority":"",
93 161 : rs->is_bad_exit?" BadExit":"",
94 161 : rs->is_exit?" Exit":"",
95 161 : rs->is_fast?" Fast":"",
96 161 : rs->is_possible_guard?" Guard":"",
97 161 : rs->is_hs_dir?" HSDir":"",
98 161 : rs->is_flagged_running?" Running":"",
99 161 : rs->is_stable?" Stable":"",
100 161 : rs->is_staledesc?" StaleDesc":"",
101 161 : rs->is_sybil?" Sybil":"",
102 161 : rs->is_v2_dir?" V2Dir":"",
103 161 : rs->is_valid?" Valid":"");
104 :
105 : /* length of "opt v \n" */
106 : #define V_LINE_OVERHEAD 7
107 161 : if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) {
108 108 : smartlist_add_asprintf(chunks, "v %s\n", version);
109 : }
110 161 : if (protocols) {
111 108 : smartlist_add_asprintf(chunks, "pr %s\n", protocols);
112 : }
113 :
114 161 : if (format != NS_V2) {
115 161 : const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest);
116 161 : uint32_t bw_kb;
117 :
118 161 : if (format != NS_CONTROL_PORT) {
119 : /* Blow up more or less nicely if we didn't get anything or not the
120 : * thing we expected.
121 : * This should be kept in sync with the function
122 : * routerstatus_has_visibly_changed and the struct routerstatus_t
123 : */
124 108 : if (!desc) {
125 0 : char id[HEX_DIGEST_LEN+1];
126 0 : char dd[HEX_DIGEST_LEN+1];
127 :
128 0 : base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
129 0 : base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN);
130 0 : log_warn(LD_BUG, "Cannot get any descriptor for %s "
131 : "(wanted descriptor %s).",
132 : id, dd);
133 0 : goto err;
134 : }
135 :
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 108 : if (tor_memneq(desc->cache_info.signed_descriptor_digest,
141 : rs->descriptor_digest,
142 : DIGEST_LEN)) {
143 0 : char rl_d[HEX_DIGEST_LEN+1];
144 0 : char rs_d[HEX_DIGEST_LEN+1];
145 0 : char id[HEX_DIGEST_LEN+1];
146 :
147 0 : base16_encode(rl_d, sizeof(rl_d),
148 : desc->cache_info.signed_descriptor_digest, DIGEST_LEN);
149 0 : base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN);
150 0 : base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
151 0 : log_err(LD_BUG, "descriptor digest in routerlist does not match "
152 : "the one in routerstatus: %s vs %s "
153 : "(router %s)\n",
154 : rl_d, rs_d, id);
155 :
156 0 : tor_assert(tor_memeq(desc->cache_info.signed_descriptor_digest,
157 : rs->descriptor_digest,
158 : DIGEST_LEN));
159 : }
160 : }
161 :
162 161 : if (format == NS_CONTROL_PORT && rs->has_bandwidth) {
163 53 : bw_kb = rs->bandwidth_kb;
164 : } else {
165 108 : tor_assert(desc);
166 108 : bw_kb = router_get_advertised_bandwidth_capped(desc) / 1000;
167 : }
168 161 : smartlist_add_asprintf(chunks,
169 : "w Bandwidth=%d", bw_kb);
170 :
171 161 : if (format == NS_V3_VOTE && vrs && vrs->has_measured_bw) {
172 12 : smartlist_add_asprintf(chunks,
173 12 : " Measured=%d", vrs->measured_bw_kb);
174 : }
175 : /* Write down guardfraction information if we have it. */
176 161 : if (format == NS_V3_VOTE && vrs && vrs->status.has_guardfraction) {
177 0 : smartlist_add_asprintf(chunks,
178 : " GuardFraction=%d",
179 0 : vrs->status.guardfraction_percentage);
180 : }
181 :
182 161 : smartlist_add_strdup(chunks, "\n");
183 :
184 161 : if (desc) {
185 108 : summary = policy_summarize(desc->exit_policy, AF_INET);
186 108 : smartlist_add_asprintf(chunks, "p %s\n", summary);
187 108 : tor_free(summary);
188 : }
189 :
190 161 : if (format == NS_V3_VOTE && vrs) {
191 108 : if (fast_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) {
192 108 : smartlist_add_strdup(chunks, "id ed25519 none\n");
193 : } else {
194 0 : char ed_b64[BASE64_DIGEST256_LEN+1];
195 0 : digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id);
196 0 : smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64);
197 : }
198 :
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 108 : time_t now = time(NULL);
203 108 : 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));
207 : }
208 : }
209 :
210 53 : done:
211 239 : result = smartlist_join_strings(chunks, "", 0, NULL);
212 :
213 239 : err:
214 1601 : SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
215 239 : smartlist_free(chunks);
216 :
217 239 : return result;
218 : }
|