fmt_routerstatus.c (9143B)
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 * If <b>declared_publish_time</b> is nonnegative, we declare it as the 30 * publication time. Otherwise we look for a publication time in <b>vrs</b>, 31 * and fall back to a default (not useful) publication time. 32 * 33 * Return 0 on success, -1 on failure. 34 * 35 * The format argument has one of the following values: 36 * NS_V2 - Output an entry suitable for a V2 NS opinion document 37 * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry 38 * for consensus_method. 39 * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc 40 * consensus entry for consensus_method. 41 * NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present, 42 * it contains additional information for the vote. 43 * NS_CONTROL_PORT - Output a NS document for the control port. 44 * 45 */ 46 char * 47 routerstatus_format_entry(const routerstatus_t *rs, const char *version, 48 const char *protocols, 49 routerstatus_format_type_t format, 50 const vote_routerstatus_t *vrs, 51 time_t declared_publish_time) 52 { 53 char *summary; 54 char *result = NULL; 55 56 char published[ISO_TIME_LEN+1]; 57 char identity64[BASE64_DIGEST_LEN+1]; 58 char digest64[BASE64_DIGEST_LEN+1]; 59 smartlist_t *chunks = smartlist_new(); 60 61 if (declared_publish_time >= 0) { 62 format_iso_time(published, declared_publish_time); 63 } else if (vrs) { 64 format_iso_time(published, vrs->published_on); 65 } else { 66 strlcpy(published, "2038-01-01 00:00:00", sizeof(published)); 67 } 68 69 const char *ip_str = fmt_addr(&rs->ipv4_addr); 70 if (ip_str[0] == '\0') 71 goto err; 72 73 digest_to_base64(identity64, rs->identity_digest); 74 digest_to_base64(digest64, rs->descriptor_digest); 75 76 smartlist_add_asprintf(chunks, 77 "r %s %s %s%s%s %s %" PRIu16 " %" PRIu16 "\n", 78 rs->nickname, 79 identity64, 80 (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64, 81 (format==NS_V3_CONSENSUS_MICRODESC)?"":" ", 82 published, 83 ip_str, 84 rs->ipv4_orport, 85 rs->ipv4_dirport); 86 87 /* TODO: Maybe we want to pass in what we need to build the rest of 88 * this here, instead of in the caller. Then we could use the 89 * networkstatus_type_t values, with an additional control port value 90 * added -MP */ 91 92 /* Possible "a" line. At most one for now. */ 93 if (!tor_addr_is_null(&rs->ipv6_addr)) { 94 smartlist_add_asprintf(chunks, "a %s\n", 95 fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport)); 96 } 97 98 if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC) 99 goto done; 100 101 smartlist_add_asprintf(chunks, 102 "s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", 103 /* These must stay in alphabetical order. */ 104 rs->is_authority?" Authority":"", 105 rs->is_bad_exit?" BadExit":"", 106 rs->is_exit?" Exit":"", 107 rs->is_fast?" Fast":"", 108 rs->is_possible_guard?" Guard":"", 109 rs->is_hs_dir?" HSDir":"", 110 rs->is_middle_only?" MiddleOnly":"", 111 rs->is_flagged_running?" Running":"", 112 rs->is_stable?" Stable":"", 113 rs->is_staledesc?" StaleDesc":"", 114 rs->is_sybil?" Sybil":"", 115 rs->is_v2_dir?" V2Dir":"", 116 rs->is_valid?" Valid":""); 117 118 /* length of "opt v \n" */ 119 #define V_LINE_OVERHEAD 7 120 if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) { 121 smartlist_add_asprintf(chunks, "v %s\n", version); 122 } 123 if (protocols) { 124 smartlist_add_asprintf(chunks, "pr %s\n", protocols); 125 } 126 127 if (format != NS_V2) { 128 const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest); 129 uint32_t bw_kb; 130 131 if (format != NS_CONTROL_PORT) { 132 /* Blow up more or less nicely if we didn't get anything or not the 133 * thing we expected. 134 * This should be kept in sync with the function 135 * routerstatus_has_visibly_changed and the struct routerstatus_t 136 */ 137 if (!desc) { 138 char id[HEX_DIGEST_LEN+1]; 139 char dd[HEX_DIGEST_LEN+1]; 140 141 base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN); 142 base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN); 143 log_warn(LD_BUG, "Cannot get any descriptor for %s " 144 "(wanted descriptor %s).", 145 id, dd); 146 goto err; 147 } 148 149 /* This assert could fire for the control port, because 150 * it can request NS documents before all descriptors 151 * have been fetched. Therefore, we only do this test when 152 * format != NS_CONTROL_PORT. */ 153 if (tor_memneq(desc->cache_info.signed_descriptor_digest, 154 rs->descriptor_digest, 155 DIGEST_LEN)) { 156 char rl_d[HEX_DIGEST_LEN+1]; 157 char rs_d[HEX_DIGEST_LEN+1]; 158 char id[HEX_DIGEST_LEN+1]; 159 160 base16_encode(rl_d, sizeof(rl_d), 161 desc->cache_info.signed_descriptor_digest, DIGEST_LEN); 162 base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN); 163 base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN); 164 log_err(LD_BUG, "descriptor digest in routerlist does not match " 165 "the one in routerstatus: %s vs %s " 166 "(router %s)\n", 167 rl_d, rs_d, id); 168 169 tor_assert(tor_memeq(desc->cache_info.signed_descriptor_digest, 170 rs->descriptor_digest, 171 DIGEST_LEN)); 172 } 173 } 174 175 if (format == NS_CONTROL_PORT && rs->has_bandwidth) { 176 bw_kb = rs->bandwidth_kb; 177 } else { 178 tor_assert(desc); 179 bw_kb = router_get_advertised_bandwidth_capped(desc) / 1000; 180 } 181 smartlist_add_asprintf(chunks, 182 "w Bandwidth=%d", bw_kb); 183 184 /* Include the bandwidth weight from our external bandwidth 185 * authority, if we have one. */ 186 if (format == NS_V3_VOTE && vrs && vrs->has_measured_bw) { 187 if (!rs->is_authority) { /* normal case */ 188 smartlist_add_asprintf(chunks, 189 " Measured=%d", vrs->measured_bw_kb); 190 } else { 191 /* dir auth special case: don't give it a Measured line, so we 192 * can reserve its attention for authority-specific activities. 193 * But do include the bwauth's opinion so it can be recorded for 194 * posterity. See #40698 for details. */ 195 smartlist_add_asprintf(chunks, 196 " MeasuredButAuthority=%d", vrs->measured_bw_kb); 197 } 198 } 199 /* Write down guardfraction information if we have it. */ 200 if (format == NS_V3_VOTE && vrs && vrs->status.has_guardfraction) { 201 smartlist_add_asprintf(chunks, 202 " GuardFraction=%d", 203 vrs->status.guardfraction_percentage); 204 } 205 206 smartlist_add_strdup(chunks, "\n"); 207 208 if (desc) { 209 summary = policy_summarize(desc->exit_policy, AF_INET); 210 smartlist_add_asprintf(chunks, "p %s\n", summary); 211 tor_free(summary); 212 } 213 214 if (format == NS_V3_VOTE && vrs) { 215 if (fast_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) { 216 smartlist_add_strdup(chunks, "id ed25519 none\n"); 217 } else { 218 char ed_b64[BASE64_DIGEST256_LEN+1]; 219 digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id); 220 smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64); 221 } 222 223 /* We'll add a series of statistics to the vote per relays so we are 224 * able to assess what each authorities sees and help our health and 225 * performance work. */ 226 time_t now = time(NULL); 227 smartlist_add_asprintf(chunks, "stats wfu=%.6f tk=%lu mtbf=%.0f\n", 228 rep_hist_get_weighted_fractional_uptime(rs->identity_digest, now), 229 rep_hist_get_weighted_time_known(rs->identity_digest, now), 230 rep_hist_get_stability(rs->identity_digest, now)); 231 } 232 } 233 234 done: 235 result = smartlist_join_strings(chunks, "", 0, NULL); 236 237 err: 238 SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); 239 smartlist_free(chunks); 240 241 return result; 242 }