forked from torproject/tor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
circuituse.c
3086 lines (2779 loc) · 116 KB
/
circuituse.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file circuituse.c
* \brief Launch the right sort of circuits and attach the right streams to
* them.
*
* As distinct from circuitlist.c, which manages lookups to find circuits, and
* circuitbuild.c, which handles the logistics of circuit construction, this
* module keeps track of which streams can be attached to which circuits (in
* circuit_get_best()), and attaches streams to circuits (with
* circuit_try_attaching_streams(), connection_ap_handshake_attach_circuit(),
* and connection_ap_handshake_attach_chosen_circuit() ).
*
* This module also makes sure that we are building circuits for all of the
* predicted ports, using circuit_remove_handled_ports(),
* circuit_stream_is_being_handled(), and circuit_build_needed_cirs(). It
* handles launching circuits for specific targets using
* circuit_launch_by_extend_info().
*
* This is also where we handle expiring circuits that have been around for
* too long without actually completing, along with the circuit_build_timeout
* logic in circuitstats.c.
**/
#include "or.h"
#include "addressmap.h"
#include "bridges.h"
#include "channel.h"
#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuitstats.h"
#include "circuituse.h"
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
#include "control.h"
#include "entrynodes.h"
#include "hs_common.h"
#include "hs_client.h"
#include "hs_circuit.h"
#include "hs_ident.h"
#include "hs_stats.h"
#include "nodelist.h"
#include "networkstatus.h"
#include "policies.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
#include "config.h"
static void circuit_expire_old_circuits_clientside(void);
static void circuit_increment_failure_count(void);
/** Check whether the hidden service destination of the stream at
* <b>edge_conn</b> is the same as the destination of the circuit at
* <b>origin_circ</b>. */
static int
circuit_matches_with_rend_stream(const edge_connection_t *edge_conn,
const origin_circuit_t *origin_circ)
{
/* Check if this is a v2 rendezvous circ/stream */
if ((edge_conn->rend_data && !origin_circ->rend_data) ||
(!edge_conn->rend_data && origin_circ->rend_data) ||
(edge_conn->rend_data && origin_circ->rend_data &&
rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data),
rend_data_get_address(origin_circ->rend_data)))) {
/* this circ is not for this conn */
return 0;
}
/* Check if this is a v3 rendezvous circ/stream */
if ((edge_conn->hs_ident && !origin_circ->hs_ident) ||
(!edge_conn->hs_ident && origin_circ->hs_ident) ||
(edge_conn->hs_ident && origin_circ->hs_ident &&
!ed25519_pubkey_eq(&edge_conn->hs_ident->identity_pk,
&origin_circ->hs_ident->identity_pk))) {
/* this circ is not for this conn */
return 0;
}
return 1;
}
/** Return 1 if <b>circ</b> could be returned by circuit_get_best().
* Else return 0.
*/
static int
circuit_is_acceptable(const origin_circuit_t *origin_circ,
const entry_connection_t *conn,
int must_be_open, uint8_t purpose,
int need_uptime, int need_internal,
time_t now)
{
const circuit_t *circ = TO_CIRCUIT(origin_circ);
const node_t *exitnode;
cpath_build_state_t *build_state;
tor_assert(circ);
tor_assert(conn);
tor_assert(conn->socks_request);
if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_chan))
return 0; /* ignore non-open circs */
if (circ->marked_for_close)
return 0;
/* if this circ isn't our purpose, skip. */
if (purpose == CIRCUIT_PURPOSE_C_REND_JOINED && !must_be_open) {
if (circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
circ->purpose != CIRCUIT_PURPOSE_C_REND_READY &&
circ->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED &&
circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED)
return 0;
} else if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT &&
!must_be_open) {
if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCING &&
circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
return 0;
} else {
if (purpose != circ->purpose)
return 0;
}
/* If this is a timed-out hidden service circuit, skip it. */
if (origin_circ->hs_circ_has_timed_out) {
return 0;
}
if (purpose == CIRCUIT_PURPOSE_C_GENERAL ||
purpose == CIRCUIT_PURPOSE_C_HSDIR_GET ||
purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
purpose == CIRCUIT_PURPOSE_HS_VANGUARDS ||
purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
if (circ->timestamp_dirty &&
circ->timestamp_dirty+get_options()->MaxCircuitDirtiness <= now)
return 0;
}
if (origin_circ->unusable_for_new_conns)
return 0;
/* decide if this circ is suitable for this conn */
/* for rend circs, circ->cpath->prev is not the last router in the
* circuit, it's the magical extra service hop. so just check the nickname
* of the one we meant to finish at.
*/
build_state = origin_circ->build_state;
exitnode = build_state_get_exit_node(build_state);
if (need_uptime && !build_state->need_uptime)
return 0;
if (need_internal != build_state->is_internal)
return 0;
if (purpose == CIRCUIT_PURPOSE_C_GENERAL ||
purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
purpose == CIRCUIT_PURPOSE_C_HSDIR_GET) {
tor_addr_t addr;
const int family = tor_addr_parse(&addr, conn->socks_request->address);
if (!exitnode && !build_state->onehop_tunnel) {
log_debug(LD_CIRC,"Not considering circuit with unknown router.");
return 0; /* this circuit is screwed and doesn't know it yet,
* or is a rendezvous circuit. */
}
if (build_state->onehop_tunnel) {
if (!conn->want_onehop) {
log_debug(LD_CIRC,"Skipping one-hop circuit.");
return 0;
}
tor_assert(conn->chosen_exit_name);
if (build_state->chosen_exit) {
char digest[DIGEST_LEN];
if (hexdigest_to_digest(conn->chosen_exit_name, digest) < 0)
return 0; /* broken digest, we don't want it */
if (tor_memneq(digest, build_state->chosen_exit->identity_digest,
DIGEST_LEN))
return 0; /* this is a circuit to somewhere else */
if (tor_digest_is_zero(digest)) {
/* we don't know the digest; have to compare addr:port */
if (family < 0 ||
!tor_addr_eq(&build_state->chosen_exit->addr, &addr) ||
build_state->chosen_exit->port != conn->socks_request->port)
return 0;
}
}
} else {
if (conn->want_onehop) {
/* don't use three-hop circuits -- that could hurt our anonymity. */
return 0;
}
}
if (origin_circ->prepend_policy && family != -1) {
int r = compare_tor_addr_to_addr_policy(&addr,
conn->socks_request->port,
origin_circ->prepend_policy);
if (r == ADDR_POLICY_REJECTED)
return 0;
}
if (exitnode && !connection_ap_can_use_exit(conn, exitnode)) {
/* can't exit from this router */
return 0;
}
} else { /* not general: this might be a rend circuit */
const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
if (!circuit_matches_with_rend_stream(edge_conn, origin_circ)) {
return 0;
}
}
if (!connection_edge_compatible_with_circuit(conn, origin_circ)) {
/* conn needs to be isolated from other conns that have already used
* origin_circ */
return 0;
}
return 1;
}
/** Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for
* <b>conn</b>, and return 0 otherwise. Used by circuit_get_best.
*/
static int
circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
const entry_connection_t *conn)
{
const circuit_t *a = TO_CIRCUIT(oa);
const circuit_t *b = TO_CIRCUIT(ob);
const uint8_t purpose = ENTRY_TO_CONN(conn)->purpose;
int a_bits, b_bits;
/* If one of the circuits was allowed to live due to relaxing its timeout,
* it is definitely worse (it's probably a much slower path). */
if (oa->relaxed_timeout && !ob->relaxed_timeout)
return 0; /* ob is better. It's not relaxed. */
if (!oa->relaxed_timeout && ob->relaxed_timeout)
return 1; /* oa is better. It's not relaxed. */
switch (purpose) {
case CIRCUIT_PURPOSE_S_HSDIR_POST:
case CIRCUIT_PURPOSE_C_HSDIR_GET:
case CIRCUIT_PURPOSE_C_GENERAL:
/* if it's used but less dirty it's best;
* else if it's more recently created it's best
*/
if (b->timestamp_dirty) {
if (a->timestamp_dirty &&
a->timestamp_dirty > b->timestamp_dirty)
return 1;
} else {
if (a->timestamp_dirty ||
timercmp(&a->timestamp_began, &b->timestamp_began, OP_GT))
return 1;
if (ob->build_state->is_internal)
/* XXXX++ what the heck is this internal thing doing here. I
* think we can get rid of it. circuit_is_acceptable() already
* makes sure that is_internal is exactly what we need it to
* be. -RD */
return 1;
}
break;
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
/* the closer it is to ack_wait the better it is */
if (a->purpose > b->purpose)
return 1;
break;
case CIRCUIT_PURPOSE_C_REND_JOINED:
/* the closer it is to rend_joined the better it is */
if (a->purpose > b->purpose)
return 1;
break;
}
/* XXXX Maybe this check should get a higher priority to avoid
* using up circuits too rapidly. */
a_bits = connection_edge_update_circuit_isolation(conn,
(origin_circuit_t*)oa, 1);
b_bits = connection_edge_update_circuit_isolation(conn,
(origin_circuit_t*)ob, 1);
/* if x_bits < 0, then we have not used x for anything; better not to dirty
* a connection if we can help it. */
if (a_bits < 0) {
return 0;
} else if (b_bits < 0) {
return 1;
}
a_bits &= ~ oa->isolation_flags_mixed;
a_bits &= ~ ob->isolation_flags_mixed;
if (n_bits_set_u8(a_bits) < n_bits_set_u8(b_bits)) {
/* The fewer new restrictions we need to make on a circuit for stream
* isolation, the better. */
return 1;
}
return 0;
}
/** Find the best circ that conn can use, preferably one which is
* dirty. Circ must not be too old.
*
* Conn must be defined.
*
* If must_be_open, ignore circs not in CIRCUIT_STATE_OPEN.
*
* circ_purpose specifies what sort of circuit we must have.
* It can be C_GENERAL, C_INTRODUCE_ACK_WAIT, or C_REND_JOINED.
*
* If it's REND_JOINED and must_be_open==0, then return the closest
* rendezvous-purposed circuit that you can find.
*
* If it's INTRODUCE_ACK_WAIT and must_be_open==0, then return the
* closest introduce-purposed circuit that you can find.
*/
static origin_circuit_t *
circuit_get_best(const entry_connection_t *conn,
int must_be_open, uint8_t purpose,
int need_uptime, int need_internal)
{
origin_circuit_t *best=NULL;
struct timeval now;
int intro_going_on_but_too_old = 0;
tor_assert(conn);
tor_assert(purpose == CIRCUIT_PURPOSE_C_GENERAL ||
purpose == CIRCUIT_PURPOSE_HS_VANGUARDS ||
purpose == CIRCUIT_PURPOSE_C_HSDIR_GET ||
purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT ||
purpose == CIRCUIT_PURPOSE_C_REND_JOINED);
tor_gettimeofday(&now);
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
origin_circuit_t *origin_circ;
if (!CIRCUIT_IS_ORIGIN(circ))
continue;
origin_circ = TO_ORIGIN_CIRCUIT(circ);
/* Log an info message if we're going to launch a new intro circ in
* parallel */
if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT &&
!must_be_open && origin_circ->hs_circ_has_timed_out &&
!circ->marked_for_close) {
intro_going_on_but_too_old = 1;
continue;
}
if (!circuit_is_acceptable(origin_circ,conn,must_be_open,purpose,
need_uptime,need_internal, (time_t)now.tv_sec))
continue;
/* now this is an acceptable circ to hand back. but that doesn't
* mean it's the *best* circ to hand back. try to decide.
*/
if (!best || circuit_is_better(origin_circ,best,conn))
best = origin_circ;
}
SMARTLIST_FOREACH_END(circ);
if (!best && intro_going_on_but_too_old)
log_info(LD_REND|LD_CIRC, "There is an intro circuit being created "
"right now, but it has already taken quite a while. Starting "
"one in parallel.");
return best;
}
/** Return the number of not-yet-open general-purpose origin circuits. */
static int
count_pending_general_client_circuits(void)
{
int count = 0;
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
if (circ->marked_for_close ||
circ->state == CIRCUIT_STATE_OPEN ||
circ->purpose != CIRCUIT_PURPOSE_C_GENERAL ||
!CIRCUIT_IS_ORIGIN(circ))
continue;
++count;
}
SMARTLIST_FOREACH_END(circ);
return count;
}
#if 0
/** Check whether, according to the policies in <b>options</b>, the
* circuit <b>circ</b> makes sense. */
/* XXXX currently only checks Exclude{Exit}Nodes; it should check more.
* Also, it doesn't have the right definition of an exit circuit. Also,
* it's never called. */
int
circuit_conforms_to_options(const origin_circuit_t *circ,
const or_options_t *options)
{
const crypt_path_t *cpath, *cpath_next = NULL;
/* first check if it includes any excluded nodes */
for (cpath = circ->cpath; cpath_next != circ->cpath; cpath = cpath_next) {
cpath_next = cpath->next;
if (routerset_contains_extendinfo(options->ExcludeNodes,
cpath->extend_info))
return 0;
}
/* then consider the final hop */
if (routerset_contains_extendinfo(options->ExcludeExitNodes,
circ->cpath->prev->extend_info))
return 0;
return 1;
}
#endif /* 0 */
/**
* Close all circuits that start at us, aren't open, and were born
* at least CircuitBuildTimeout seconds ago.
*
* TODO: This function is now partially redundant to
* circuit_build_times_handle_completed_hop(), but that function only
* covers circuits up to and including 3 hops that are still actually
* completing hops. However, circuit_expire_building() also handles longer
* circuits, as well as circuits that are completely stalled.
* In the future (after prop247/other path selection revamping), we probably
* want to eliminate this rats nest in favor of a simpler approach.
*/
void
circuit_expire_building(void)
{
/* circ_times.timeout_ms and circ_times.close_ms are from
* circuit_build_times_get_initial_timeout() if we haven't computed
* custom timeouts yet */
struct timeval general_cutoff, begindir_cutoff, fourhop_cutoff,
close_cutoff, extremely_old_cutoff, hs_extremely_old_cutoff,
cannibalized_cutoff, c_intro_cutoff, s_intro_cutoff, stream_cutoff;
const or_options_t *options = get_options();
struct timeval now;
cpath_build_state_t *build_state;
int any_opened_circs = 0;
tor_gettimeofday(&now);
/* Check to see if we have any opened circuits. If we don't,
* we want to be more lenient with timeouts, in case the
* user has relocated and/or changed network connections.
* See bug #3443. */
any_opened_circs = circuit_any_opened_circuits();
#define SET_CUTOFF(target, msec) do { \
long ms = tor_lround(msec); \
struct timeval diff; \
diff.tv_sec = ms / 1000; \
diff.tv_usec = (int)((ms % 1000) * 1000); \
timersub(&now, &diff, &target); \
} while (0)
/**
* Because circuit build timeout is calculated only based on 3 hop
* general purpose circuit construction, we need to scale the timeout
* to make it properly apply to longer circuits, and circuits of
* certain usage types. The following diagram illustrates how we
* derive the scaling below. In short, we calculate the number
* of times our telescoping-based circuit construction causes cells
* to traverse each link for the circuit purpose types in question,
* and then assume each link is equivalent.
*
* OP --a--> A --b--> B --c--> C
* OP --a--> A --b--> B --c--> C --d--> D
*
* Let h = a = b = c = d
*
* Three hops (general_cutoff)
* RTTs = 3a + 2b + c
* RTTs = 6h
* Cannibalized:
* RTTs = a+b+c+d
* RTTs = 4h
* Four hops:
* RTTs = 4a + 3b + 2c + d
* RTTs = 10h
* Client INTRODUCE1+ACK: // XXX: correct?
* RTTs = 5a + 4b + 3c + 2d
* RTTs = 14h
* Server intro:
* RTTs = 4a + 3b + 2c
* RTTs = 9h
*/
SET_CUTOFF(general_cutoff, get_circuit_build_timeout_ms());
SET_CUTOFF(begindir_cutoff, get_circuit_build_timeout_ms());
// TODO: We should probably use route_len_for_purpose() here instead,
// except that does not count the extra round trip for things like server
// intros and rends.
/* > 3hop circs seem to have a 1.0 second delay on their cannibalized
* 4th hop. */
SET_CUTOFF(fourhop_cutoff, get_circuit_build_timeout_ms() * (10/6.0) + 1000);
/* CIRCUIT_PURPOSE_C_ESTABLISH_REND behaves more like a RELAY cell.
* Use the stream cutoff (more or less). */
SET_CUTOFF(stream_cutoff, MAX(options->CircuitStreamTimeout,15)*1000 + 1000);
/* Be lenient with cannibalized circs. They already survived the official
* CBT, and they're usually not performance-critical. */
SET_CUTOFF(cannibalized_cutoff,
MAX(get_circuit_build_close_time_ms()*(4/6.0),
options->CircuitStreamTimeout * 1000) + 1000);
/* Intro circs have an extra round trip (and are also 4 hops long) */
SET_CUTOFF(c_intro_cutoff, get_circuit_build_timeout_ms() * (14/6.0) + 1000);
/* Server intro circs have an extra round trip */
SET_CUTOFF(s_intro_cutoff, get_circuit_build_timeout_ms() * (9/6.0) + 1000);
SET_CUTOFF(close_cutoff, get_circuit_build_close_time_ms());
SET_CUTOFF(extremely_old_cutoff, get_circuit_build_close_time_ms()*2 + 1000);
SET_CUTOFF(hs_extremely_old_cutoff,
MAX(get_circuit_build_close_time_ms()*2 + 1000,
options->SocksTimeout * 1000));
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *,victim) {
struct timeval cutoff;
if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */
victim->marked_for_close) /* don't mess with marked circs */
continue;
/* If we haven't yet started the first hop, it means we don't have
* any orconns available, and thus have not started counting time yet
* for this circuit. See circuit_deliver_create_cell() and uses of
* timestamp_began.
*
* Continue to wait in this case. The ORConn should timeout
* independently and kill us then.
*/
if (TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_CLOSED) {
continue;
}
build_state = TO_ORIGIN_CIRCUIT(victim)->build_state;
if (build_state && build_state->onehop_tunnel)
cutoff = begindir_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
cutoff = close_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
cutoff = c_intro_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
cutoff = s_intro_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND)
cutoff = stream_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
cutoff = close_cutoff;
else if (TO_ORIGIN_CIRCUIT(victim)->has_opened &&
victim->state != CIRCUIT_STATE_OPEN)
cutoff = cannibalized_cutoff;
else if (build_state && build_state->desired_path_len >= 4)
cutoff = fourhop_cutoff;
else
cutoff = general_cutoff;
if (TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)
cutoff = hs_extremely_old_cutoff;
if (timercmp(&victim->timestamp_began, &cutoff, OP_GT))
continue; /* it's still young, leave it alone */
/* We need to double-check the opened state here because
* we don't want to consider opened 1-hop dircon circuits for
* deciding when to relax the timeout, but we *do* want to relax
* those circuits too if nothing else is opened *and* they still
* aren't either. */
if (!any_opened_circs && victim->state != CIRCUIT_STATE_OPEN) {
/* It's still young enough that we wouldn't close it, right? */
if (timercmp(&victim->timestamp_began, &close_cutoff, OP_GT)) {
if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) {
int first_hop_succeeded = TO_ORIGIN_CIRCUIT(victim)->cpath->state
== CPATH_STATE_OPEN;
log_info(LD_CIRC,
"No circuits are opened. Relaxing timeout for circuit %d "
"(a %s %d-hop circuit in state %s with channel state %s).",
TO_ORIGIN_CIRCUIT(victim)->global_identifier,
circuit_purpose_to_string(victim->purpose),
TO_ORIGIN_CIRCUIT(victim)->build_state ?
TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len :
-1,
circuit_state_to_string(victim->state),
victim->n_chan ?
channel_state_to_string(victim->n_chan->state) : "none");
/* We count the timeout here for CBT, because technically this
* was a timeout, and the timeout value needs to reset if we
* see enough of them. Note this means we also need to avoid
* double-counting below, too. */
circuit_build_times_count_timeout(get_circuit_build_times_mutable(),
first_hop_succeeded);
TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout = 1;
}
continue;
} else {
static ratelim_t relax_timeout_limit = RATELIM_INIT(3600);
const double build_close_ms = get_circuit_build_close_time_ms();
log_fn_ratelim(&relax_timeout_limit, LOG_NOTICE, LD_CIRC,
"No circuits are opened. Relaxed timeout for circuit %d "
"(a %s %d-hop circuit in state %s with channel state %s) to "
"%ldms. However, it appears the circuit has timed out "
"anyway.",
TO_ORIGIN_CIRCUIT(victim)->global_identifier,
circuit_purpose_to_string(victim->purpose),
TO_ORIGIN_CIRCUIT(victim)->build_state ?
TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len :
-1,
circuit_state_to_string(victim->state),
victim->n_chan ?
channel_state_to_string(victim->n_chan->state) : "none",
(long)build_close_ms);
}
}
#if 0
/* some debug logs, to help track bugs */
if (victim->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
victim->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) {
if (!victim->timestamp_dirty)
log_fn(LOG_DEBUG,"Considering %sopen purpose %d to %s (circid %d)."
"(clean).",
victim->state == CIRCUIT_STATE_OPEN ? "" : "non",
victim->purpose, victim->build_state->chosen_exit_name,
victim->n_circ_id);
else
log_fn(LOG_DEBUG,"Considering %sopen purpose %d to %s (circid %d). "
"%d secs since dirty.",
victim->state == CIRCUIT_STATE_OPEN ? "" : "non",
victim->purpose, victim->build_state->chosen_exit_name,
victim->n_circ_id,
(int)(now - victim->timestamp_dirty));
}
#endif /* 0 */
/* if circ is !open, or if it's open but purpose is a non-finished
* intro or rend, then mark it for close */
if (victim->state == CIRCUIT_STATE_OPEN) {
switch (victim->purpose) {
default: /* most open circuits can be left alone. */
continue; /* yes, continue inside a switch refers to the nearest
* enclosing loop. C is smart. */
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
break; /* too old, need to die */
case CIRCUIT_PURPOSE_C_REND_READY:
/* it's a rend_ready circ -- has it already picked a query? */
/* c_rend_ready circs measure age since timestamp_dirty,
* because that's set when they switch purposes
*/
if (TO_ORIGIN_CIRCUIT(victim)->rend_data ||
TO_ORIGIN_CIRCUIT(victim)->hs_ident ||
victim->timestamp_dirty > cutoff.tv_sec)
continue;
break;
case CIRCUIT_PURPOSE_PATH_BIAS_TESTING:
/* Open path bias testing circuits are given a long
* time to complete the test, but not forever */
TO_ORIGIN_CIRCUIT(victim)->path_state = PATH_STATE_USE_FAILED;
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
/* That purpose means that the intro point circuit has been opened
* succesfully but the INTRODUCE1 cell hasn't been sent yet because
* the client is waiting for the rendezvous point circuit to open.
* Keep this circuit open while waiting for the rendezvous circuit.
* We let the circuit idle timeout take care of cleaning this
* circuit if it never used. */
continue;
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
/* rend and intro circs become dirty each time they
* make an introduction attempt. so timestamp_dirty
* will reflect the time since the last attempt.
*/
if (victim->timestamp_dirty > cutoff.tv_sec)
continue;
break;
}
} else { /* circuit not open, consider recording failure as timeout */
int first_hop_succeeded = TO_ORIGIN_CIRCUIT(victim)->cpath &&
TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_OPEN;
if (TO_ORIGIN_CIRCUIT(victim)->p_streams != NULL) {
log_warn(LD_BUG, "Circuit %d (purpose %d, %s) has timed out, "
"yet has attached streams!",
TO_ORIGIN_CIRCUIT(victim)->global_identifier,
victim->purpose,
circuit_purpose_to_string(victim->purpose));
tor_fragile_assert();
continue;
}
if (circuit_timeout_want_to_count_circ(TO_ORIGIN_CIRCUIT(victim)) &&
circuit_build_times_enough_to_compute(get_circuit_build_times())) {
log_info(LD_CIRC,
"Deciding to count the timeout for circuit "U64_FORMAT"\n",
U64_PRINTF_ARG(
TO_ORIGIN_CIRCUIT(victim)->global_identifier));
/* Circuits are allowed to last longer for measurement.
* Switch their purpose and wait. */
if (victim->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
circuit_build_times_mark_circ_as_measurement_only(TO_ORIGIN_CIRCUIT(
victim));
continue;
}
/*
* If the circuit build time is much greater than we would have cut
* it off at, we probably had a suspend event along this codepath,
* and we should discard the value.
*/
if (timercmp(&victim->timestamp_began, &extremely_old_cutoff, OP_LT)) {
log_notice(LD_CIRC,
"Extremely large value for circuit build timeout: %lds. "
"Assuming clock jump. Purpose %d (%s)",
(long)(now.tv_sec - victim->timestamp_began.tv_sec),
victim->purpose,
circuit_purpose_to_string(victim->purpose));
} else if (circuit_build_times_count_close(
get_circuit_build_times_mutable(),
first_hop_succeeded,
(time_t)victim->timestamp_created.tv_sec)) {
circuit_build_times_set_timeout(get_circuit_build_times_mutable());
}
}
}
/* If this is a hidden service client circuit which is far enough along in
* connecting to its destination, and we haven't already flagged it as
* 'timed out', flag it so we'll launch another intro or rend circ, but
* don't mark it for close yet.
*
* (Circs flagged as 'timed out' are given a much longer timeout
* period above, so we won't close them in the next call to
* circuit_expire_building.) */
if (!(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)) {
switch (victim->purpose) {
case CIRCUIT_PURPOSE_C_REND_READY:
/* We only want to spare a rend circ if it has been specified in
* an INTRODUCE1 cell sent to a hidden service. A circ's
* pending_final_cpath field is non-NULL iff it is a rend circ
* and we have tried to send an INTRODUCE1 cell specifying it.
* Thus, if the pending_final_cpath field *is* NULL, then we
* want to not spare it. */
if (TO_ORIGIN_CIRCUIT(victim)->build_state &&
TO_ORIGIN_CIRCUIT(victim)->build_state->pending_final_cpath ==
NULL)
break;
/* fallthrough! */
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
/* If we have reached this line, we want to spare the circ for now. */
log_info(LD_CIRC,"Marking circ %u (state %d:%s, purpose %d) "
"as timed-out HS circ",
(unsigned)victim->n_circ_id,
victim->state, circuit_state_to_string(victim->state),
victim->purpose);
TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1;
continue;
default:
break;
}
}
/* If this is a service-side rendezvous circuit which is far
* enough along in connecting to its destination, consider sparing
* it. */
if (!(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out) &&
victim->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) {
log_info(LD_CIRC,"Marking circ %u (state %d:%s, purpose %d) "
"as timed-out HS circ; relaunching rendezvous attempt.",
(unsigned)victim->n_circ_id,
victim->state, circuit_state_to_string(victim->state),
victim->purpose);
TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1;
hs_circ_retry_service_rendezvous_point(TO_ORIGIN_CIRCUIT(victim));
continue;
}
if (victim->n_chan)
log_info(LD_CIRC,
"Abandoning circ %u %s:%u (state %d,%d:%s, purpose %d, "
"len %d)", TO_ORIGIN_CIRCUIT(victim)->global_identifier,
channel_get_canonical_remote_descr(victim->n_chan),
(unsigned)victim->n_circ_id,
TO_ORIGIN_CIRCUIT(victim)->has_opened,
victim->state, circuit_state_to_string(victim->state),
victim->purpose,
TO_ORIGIN_CIRCUIT(victim)->build_state ?
TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len :
-1);
else
log_info(LD_CIRC,
"Abandoning circ %u %u (state %d,%d:%s, purpose %d, len %d)",
TO_ORIGIN_CIRCUIT(victim)->global_identifier,
(unsigned)victim->n_circ_id,
TO_ORIGIN_CIRCUIT(victim)->has_opened,
victim->state,
circuit_state_to_string(victim->state), victim->purpose,
TO_ORIGIN_CIRCUIT(victim)->build_state ?
TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len :
-1);
circuit_log_path(LOG_INFO,LD_CIRC,TO_ORIGIN_CIRCUIT(victim));
if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
circuit_mark_for_close(victim, END_CIRC_REASON_MEASUREMENT_EXPIRED);
else
circuit_mark_for_close(victim, END_CIRC_REASON_TIMEOUT);
pathbias_count_timeout(TO_ORIGIN_CIRCUIT(victim));
} SMARTLIST_FOREACH_END(victim);
}
/**
* Mark for close all circuits that start here, that were built through a
* guard we weren't sure if we wanted to use, and that have been waiting
* around for way too long.
*/
void
circuit_expire_waiting_for_better_guard(void)
{
SMARTLIST_FOREACH_BEGIN(circuit_get_global_origin_circuit_list(),
origin_circuit_t *, circ) {
if (TO_CIRCUIT(circ)->marked_for_close)
continue;
if (circ->guard_state == NULL)
continue;
if (entry_guard_state_should_expire(circ->guard_state))
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NONE);
} SMARTLIST_FOREACH_END(circ);
}
/** For debugging #8387: track when we last called
* circuit_expire_old_circuits_clientside. */
static time_t last_expired_clientside_circuits = 0;
/**
* As a diagnostic for bug 8387, log information about how many one-hop
* circuits we have around that have been there for at least <b>age</b>
* seconds. Log a few of them.
* Ignores Single Onion Service intro and Tor2web redezvous circuits, they are
* expected to be long-term one-hop circuits.
*/
void
circuit_log_ancient_one_hop_circuits(int age)
{
#define MAX_ANCIENT_ONEHOP_CIRCUITS_TO_LOG 10
time_t now = time(NULL);
time_t cutoff = now - age;
int n_found = 0;
smartlist_t *log_these = smartlist_new();
const or_options_t *options = get_options();
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
const origin_circuit_t *ocirc;
if (! CIRCUIT_IS_ORIGIN(circ))
continue;
if (circ->timestamp_created.tv_sec >= cutoff)
continue;
/* Single Onion Services deliberately make long term one-hop intro
* connections. We only ignore active intro point connections, if we take
* a long time establishing, that's worth logging. */
if (rend_service_allow_non_anonymous_connection(options) &&
circ->purpose == CIRCUIT_PURPOSE_S_INTRO)
continue;
/* Tor2web deliberately makes long term one-hop rend connections,
* particularly when Tor2webRendezvousPoints is used. We only ignore
* active rend point connections, if we take a long time to rendezvous,
* that's worth logging. */
if (rend_client_allow_non_anonymous_connection(options) &&
circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED)
continue;
ocirc = CONST_TO_ORIGIN_CIRCUIT(circ);
if (ocirc->build_state && ocirc->build_state->onehop_tunnel) {
++n_found;
if (smartlist_len(log_these) < MAX_ANCIENT_ONEHOP_CIRCUITS_TO_LOG)
smartlist_add(log_these, (origin_circuit_t*) ocirc);
}
}
SMARTLIST_FOREACH_END(circ);
if (n_found == 0)
goto done;
log_notice(LD_HEARTBEAT,
"Diagnostic for issue 8387: Found %d one-hop circuits more "
"than %d seconds old! Logging %d...",
n_found, age, smartlist_len(log_these));
SMARTLIST_FOREACH_BEGIN(log_these, const origin_circuit_t *, ocirc) {
char created[ISO_TIME_LEN+1];
int stream_num;
const edge_connection_t *conn;
char *dirty = NULL;
const circuit_t *circ = TO_CIRCUIT(ocirc);
format_local_iso_time(created,
(time_t)circ->timestamp_created.tv_sec);
if (circ->timestamp_dirty) {
char dirty_since[ISO_TIME_LEN+1];
format_local_iso_time(dirty_since, circ->timestamp_dirty);
tor_asprintf(&dirty, "Dirty since %s (%ld seconds vs %ld-second cutoff)",
dirty_since, (long)(now - circ->timestamp_dirty),
(long) options->MaxCircuitDirtiness);
} else {
dirty = tor_strdup("Not marked dirty");
}
log_notice(LD_HEARTBEAT, " #%d created at %s. %s, %s. %s for close. "
"Package window: %d. "
"%s for new conns. %s.",
ocirc_sl_idx,
created,
circuit_state_to_string(circ->state),
circuit_purpose_to_string(circ->purpose),
circ->marked_for_close ? "Marked" : "Not marked",
circ->package_window,
ocirc->unusable_for_new_conns ? "Not usable" : "usable",
dirty);
tor_free(dirty);
stream_num = 0;
for (conn = ocirc->p_streams; conn; conn = conn->next_stream) {
const connection_t *c = TO_CONN(conn);
char stream_created[ISO_TIME_LEN+1];
if (++stream_num >= 5)
break;
format_local_iso_time(stream_created, c->timestamp_created);
log_notice(LD_HEARTBEAT, " Stream#%d created at %s. "
"%s conn in state %s. "
"It is %slinked and %sreading from a linked connection %p. "
"Package window %d. "
"%s for close (%s:%d). Hold-open is %sset. "
"Has %ssent RELAY_END. %s on circuit.",
stream_num,
stream_created,
conn_type_to_string(c->type),
conn_state_to_string(c->type, c->state),
c->linked ? "" : "not ",
c->reading_from_linked_conn ? "": "not",
c->linked_conn,
conn->package_window,
c->marked_for_close ? "Marked" : "Not marked",
c->marked_for_close_file ? c->marked_for_close_file : "--",
c->marked_for_close,
c->hold_open_until_flushed ? "" : "not ",
conn->edge_has_sent_end ? "" : "not ",
conn->edge_blocked_on_circ ? "Blocked" : "Not blocked");
if (! c->linked_conn)
continue;
c = c->linked_conn;
log_notice(LD_HEARTBEAT, " Linked to %s connection in state %s "
"(Purpose %d). %s for close (%s:%d). Hold-open is %sset. ",
conn_type_to_string(c->type),
conn_state_to_string(c->type, c->state),
c->purpose,
c->marked_for_close ? "Marked" : "Not marked",
c->marked_for_close_file ? c->marked_for_close_file : "--",
c->marked_for_close,
c->hold_open_until_flushed ? "" : "not ");
}
} SMARTLIST_FOREACH_END(ocirc);
log_notice(LD_HEARTBEAT, "It has been %ld seconds since I last called "
"circuit_expire_old_circuits_clientside().",
(long)(now - last_expired_clientside_circuits));
done:
smartlist_free(log_these);
}
/** Remove any elements in <b>needed_ports</b> that are handled by an
* open or in-progress circuit.
*/
void