diff options
Diffstat (limited to 'src/transport/gnunet-service-transport_neighbours.c')
-rw-r--r-- | src/transport/gnunet-service-transport_neighbours.c | 3971 |
1 files changed, 0 insertions, 3971 deletions
diff --git a/src/transport/gnunet-service-transport_neighbours.c b/src/transport/gnunet-service-transport_neighbours.c deleted file mode 100644 index ca1b4d1da..000000000 --- a/src/transport/gnunet-service-transport_neighbours.c +++ /dev/null | |||
@@ -1,3971 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2010-2015 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file transport/gnunet-service-transport_neighbours.c | ||
23 | * @brief neighbour management | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_ats_service.h" | ||
28 | #include "gnunet-service-transport_ats.h" | ||
29 | #include "gnunet-service-transport_neighbours.h" | ||
30 | #include "gnunet-service-transport_manipulation.h" | ||
31 | #include "gnunet-service-transport_plugins.h" | ||
32 | #include "gnunet-service-transport_validation.h" | ||
33 | #include "gnunet-service-transport.h" | ||
34 | #include "gnunet_peerinfo_service.h" | ||
35 | #include "gnunet_constants.h" | ||
36 | #include "transport.h" | ||
37 | |||
38 | /** | ||
39 | * Experimental option to ignore SessionQuotaMessages from | ||
40 | * the other peer. | ||
41 | */ | ||
42 | #define IGNORE_INBOUND_QUOTA GNUNET_YES | ||
43 | |||
44 | /** | ||
45 | * Size of the neighbour hash map. | ||
46 | */ | ||
47 | #define NEIGHBOUR_TABLE_SIZE 256 | ||
48 | |||
49 | /** | ||
50 | * Time we give plugin to transmit DISCONNECT message before the | ||
51 | * neighbour entry self-destructs. | ||
52 | */ | ||
53 | #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
54 | GNUNET_TIME_UNIT_MILLISECONDS, 500) | ||
55 | |||
56 | /** | ||
57 | * How often must a peer violate bandwidth quotas before we start | ||
58 | * to simply drop its messages? | ||
59 | */ | ||
60 | #define QUOTA_VIOLATION_DROP_THRESHOLD 10 | ||
61 | |||
62 | /** | ||
63 | * How long are we willing to wait for a response from ATS before timing out? | ||
64 | */ | ||
65 | #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
66 | GNUNET_TIME_UNIT_SECONDS, 5) | ||
67 | |||
68 | /** | ||
69 | * How long are we willing to wait for an ACK from the other peer before | ||
70 | * giving up on our connect operation? | ||
71 | */ | ||
72 | #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
73 | GNUNET_TIME_UNIT_SECONDS, 15) | ||
74 | |||
75 | /** | ||
76 | * How long are we willing to wait for a successful reconnect if | ||
77 | * an existing connection went down? Much shorter than the | ||
78 | * usual SETUP_CONNECTION_TIMEOUT as we do not inform the | ||
79 | * higher layers about the disconnect during this period. | ||
80 | */ | ||
81 | #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_UNIT_SECONDS | ||
82 | |||
83 | /** | ||
84 | * Interval to send utilization data | ||
85 | */ | ||
86 | #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_UNIT_SECONDS | ||
87 | |||
88 | /** | ||
89 | * State describing which kind a reply this neighbour should send | ||
90 | */ | ||
91 | enum GST_ACK_State | ||
92 | { | ||
93 | /** | ||
94 | * We did not receive a SYN message for this neighbour | ||
95 | */ | ||
96 | ACK_UNDEFINED = 0, | ||
97 | |||
98 | /** | ||
99 | * The neighbour received a SYN message and has to send a SYN_ACK | ||
100 | * as reply | ||
101 | */ | ||
102 | ACK_SEND_SYN_ACK = 1, | ||
103 | |||
104 | /** | ||
105 | * The neighbour sent a SYN_ACK message and has to send a ACK | ||
106 | * as reply | ||
107 | */ | ||
108 | ACK_SEND_ACK = 2 | ||
109 | }; | ||
110 | |||
111 | |||
112 | GNUNET_NETWORK_STRUCT_BEGIN | ||
113 | |||
114 | /** | ||
115 | * Message a peer sends to another to indicate that it intends to | ||
116 | * setup a connection/session for data exchange. A 'SESSION_SYN' | ||
117 | * should be answered with a 'SESSION_SYN_ACK' with the same body | ||
118 | * to confirm. A 'SESSION_SYN_ACK' should then be followed with | ||
119 | * a 'ACK'. Once the 'ACK' is received, both peers | ||
120 | * should be connected. | ||
121 | */ | ||
122 | struct TransportSynMessage | ||
123 | { | ||
124 | /** | ||
125 | * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN | ||
126 | * or #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK | ||
127 | */ | ||
128 | struct GNUNET_MessageHeader header; | ||
129 | |||
130 | /** | ||
131 | * Always zero. | ||
132 | */ | ||
133 | uint32_t reserved GNUNET_PACKED; | ||
134 | |||
135 | /** | ||
136 | * Absolute time at the sender. Only the most recent connect | ||
137 | * message implies which session is preferred by the sender. | ||
138 | */ | ||
139 | struct GNUNET_TIME_AbsoluteNBO timestamp; | ||
140 | }; | ||
141 | |||
142 | |||
143 | /** | ||
144 | * Message a peer sends to another when connected to indicate that a | ||
145 | * session is in use and the peer is still alive or to respond to a keep alive. | ||
146 | * A peer sends a message with type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE | ||
147 | * to request a message with #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE. | ||
148 | * When the keep alive response with type is received, transport service | ||
149 | * will call the respective plugin to update the session timeout | ||
150 | */ | ||
151 | struct GNUNET_ATS_SessionKeepAliveMessage | ||
152 | { | ||
153 | /** | ||
154 | * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE or | ||
155 | * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE. | ||
156 | */ | ||
157 | struct GNUNET_MessageHeader header; | ||
158 | |||
159 | /** | ||
160 | * A nonce to identify the session the keep alive is used for | ||
161 | */ | ||
162 | uint32_t nonce GNUNET_PACKED; | ||
163 | }; | ||
164 | |||
165 | |||
166 | /** | ||
167 | * Message a peer sends to another when connected to indicate that | ||
168 | * the other peer should limit transmissions to the indicated | ||
169 | * quota. | ||
170 | */ | ||
171 | struct GNUNET_ATS_SessionQuotaMessage | ||
172 | { | ||
173 | /** | ||
174 | * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA. | ||
175 | */ | ||
176 | struct GNUNET_MessageHeader header; | ||
177 | |||
178 | /** | ||
179 | * Quota to use (for sending), in bytes per second. | ||
180 | */ | ||
181 | uint32_t quota GNUNET_PACKED; | ||
182 | }; | ||
183 | |||
184 | |||
185 | /** | ||
186 | * Message we send to the other peer to notify it that we intentionally | ||
187 | * are disconnecting (to reduce timeouts). This is just a friendly | ||
188 | * notification, peers must not rely on always receiving disconnect | ||
189 | * messages. | ||
190 | */ | ||
191 | struct GNUNET_ATS_SessionDisconnectMessage | ||
192 | { | ||
193 | /** | ||
194 | * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT | ||
195 | */ | ||
196 | struct GNUNET_MessageHeader header; | ||
197 | |||
198 | /** | ||
199 | * Always zero. | ||
200 | */ | ||
201 | uint32_t reserved GNUNET_PACKED; | ||
202 | |||
203 | /** | ||
204 | * Purpose of the signature. Extends over the timestamp. | ||
205 | * Purpose should be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT. | ||
206 | */ | ||
207 | struct GNUNET_CRYPTO_EccSignaturePurpose purpose; | ||
208 | |||
209 | /** | ||
210 | * Absolute time at the sender. Only the most recent connect | ||
211 | * message implies which session is preferred by the sender. | ||
212 | */ | ||
213 | struct GNUNET_TIME_AbsoluteNBO timestamp; | ||
214 | |||
215 | /** | ||
216 | * Public key of the sender. | ||
217 | */ | ||
218 | struct GNUNET_CRYPTO_EddsaPublicKey public_key; | ||
219 | |||
220 | /** | ||
221 | * Signature of the peer that sends us the disconnect. Only | ||
222 | * valid if the timestamp is AFTER the timestamp from the | ||
223 | * corresponding 'SYN' message. | ||
224 | */ | ||
225 | struct GNUNET_CRYPTO_EddsaSignature signature; | ||
226 | }; | ||
227 | |||
228 | GNUNET_NETWORK_STRUCT_END | ||
229 | |||
230 | |||
231 | /** | ||
232 | * For each neighbour we keep a list of messages | ||
233 | * that we still want to transmit to the neighbour. | ||
234 | */ | ||
235 | struct MessageQueue | ||
236 | { | ||
237 | /** | ||
238 | * This is a doubly linked list. | ||
239 | */ | ||
240 | struct MessageQueue *next; | ||
241 | |||
242 | /** | ||
243 | * This is a doubly linked list. | ||
244 | */ | ||
245 | struct MessageQueue *prev; | ||
246 | |||
247 | /** | ||
248 | * Function to call once we're done. | ||
249 | */ | ||
250 | GST_NeighbourSendContinuation cont; | ||
251 | |||
252 | /** | ||
253 | * Closure for @e cont | ||
254 | */ | ||
255 | void *cont_cls; | ||
256 | |||
257 | /** | ||
258 | * The message(s) we want to transmit, GNUNET_MessageHeader(s) | ||
259 | * stuck together in memory. Allocated at the end of this struct. | ||
260 | */ | ||
261 | const char *message_buf; | ||
262 | |||
263 | /** | ||
264 | * Size of the message buf | ||
265 | */ | ||
266 | size_t message_buf_size; | ||
267 | |||
268 | /** | ||
269 | * At what time should we fail? | ||
270 | */ | ||
271 | struct GNUNET_TIME_Absolute timeout; | ||
272 | }; | ||
273 | |||
274 | |||
275 | /** | ||
276 | * A possible address we could use to communicate with a neighbour. | ||
277 | */ | ||
278 | struct NeighbourAddress | ||
279 | { | ||
280 | /** | ||
281 | * Active session for this address. | ||
282 | */ | ||
283 | struct GNUNET_ATS_Session *session; | ||
284 | |||
285 | /** | ||
286 | * Network-level address information. | ||
287 | */ | ||
288 | struct GNUNET_HELLO_Address *address; | ||
289 | |||
290 | /** | ||
291 | * Timestamp of the 'SESSION_CONNECT' message we sent to the other | ||
292 | * peer for this address. Use to check that the ACK is in response | ||
293 | * to our most recent 'SYN'. | ||
294 | */ | ||
295 | struct GNUNET_TIME_Absolute connect_timestamp; | ||
296 | |||
297 | /** | ||
298 | * Inbound bandwidth from ATS for this address. | ||
299 | */ | ||
300 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in; | ||
301 | |||
302 | /** | ||
303 | * Outbound bandwidth from ATS for this address. | ||
304 | */ | ||
305 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out; | ||
306 | |||
307 | /** | ||
308 | * Did we tell ATS that this is our 'active' address? | ||
309 | */ | ||
310 | int ats_active; | ||
311 | |||
312 | /** | ||
313 | * The current nonce sent in the last keep alive messages | ||
314 | */ | ||
315 | uint32_t keep_alive_nonce; | ||
316 | }; | ||
317 | |||
318 | |||
319 | /** | ||
320 | * Entry in neighbours. | ||
321 | */ | ||
322 | struct NeighbourMapEntry | ||
323 | { | ||
324 | /** | ||
325 | * Head of list of messages we would like to send to this peer; | ||
326 | * must contain at most one message per client. | ||
327 | */ | ||
328 | struct MessageQueue *messages_head; | ||
329 | |||
330 | /** | ||
331 | * Tail of list of messages we would like to send to this peer; must | ||
332 | * contain at most one message per client. | ||
333 | */ | ||
334 | struct MessageQueue *messages_tail; | ||
335 | |||
336 | /** | ||
337 | * Are we currently trying to send a message? If so, which one? | ||
338 | */ | ||
339 | struct MessageQueue *is_active; | ||
340 | |||
341 | /** | ||
342 | * Primary address we currently use to communicate with the neighbour. | ||
343 | */ | ||
344 | struct NeighbourAddress primary_address; | ||
345 | |||
346 | /** | ||
347 | * Alternative address currently under consideration for communicating | ||
348 | * with the neighbour. | ||
349 | */ | ||
350 | struct NeighbourAddress alternative_address; | ||
351 | |||
352 | /** | ||
353 | * Identity of this neighbour. | ||
354 | */ | ||
355 | struct GNUNET_PeerIdentity id; | ||
356 | |||
357 | /** | ||
358 | * Main task that drives this peer (timeouts, keepalives, etc.). | ||
359 | * Always runs the #master_task(). | ||
360 | */ | ||
361 | struct GNUNET_SCHEDULER_Task *task; | ||
362 | |||
363 | /** | ||
364 | * Task to disconnect neighbour after we received a DISCONNECT message | ||
365 | */ | ||
366 | struct GNUNET_SCHEDULER_Task *delayed_disconnect_task; | ||
367 | |||
368 | /** | ||
369 | * At what time should we sent the next keep-alive message? | ||
370 | */ | ||
371 | struct GNUNET_TIME_Absolute keep_alive_time; | ||
372 | |||
373 | /** | ||
374 | * At what time did we sent the last keep-alive message? Used | ||
375 | * to calculate round-trip time ("latency"). | ||
376 | */ | ||
377 | struct GNUNET_TIME_Absolute last_keep_alive_time; | ||
378 | |||
379 | /** | ||
380 | * Timestamp we should include in our next SYN_ACK message. | ||
381 | * (only valid if 'send_connect_ack' is #GNUNET_YES). Used to build | ||
382 | * our SYN_ACK message. | ||
383 | */ | ||
384 | struct GNUNET_TIME_Absolute connect_ack_timestamp; | ||
385 | |||
386 | /** | ||
387 | * ATS address suggest handle | ||
388 | */ | ||
389 | struct GNUNET_ATS_ConnectivitySuggestHandle *suggest_handle; | ||
390 | |||
391 | /** | ||
392 | * Time where we should cut the connection (timeout) if we don't | ||
393 | * make progress in the state machine (or get a KEEPALIVE_RESPONSE | ||
394 | * if we are in #GNUNET_TRANSPORT_PS_CONNECTED). | ||
395 | */ | ||
396 | struct GNUNET_TIME_Absolute timeout; | ||
397 | |||
398 | /** | ||
399 | * Tracker for inbound bandwidth. | ||
400 | */ | ||
401 | struct GNUNET_BANDWIDTH_Tracker in_tracker; | ||
402 | |||
403 | /** | ||
404 | * How often has the other peer (recently) violated the inbound | ||
405 | * traffic limit? Incremented by 10 per violation, decremented by 1 | ||
406 | * per non-violation (for each time interval). | ||
407 | */ | ||
408 | unsigned int quota_violation_count; | ||
409 | |||
410 | /** | ||
411 | * Latest quota the other peer send us in bytes per second. | ||
412 | * We should not send more, least the other peer throttle | ||
413 | * receiving our traffic. | ||
414 | */ | ||
415 | struct GNUNET_BANDWIDTH_Value32NBO neighbour_receive_quota; | ||
416 | |||
417 | /** | ||
418 | * The current state of the peer. | ||
419 | */ | ||
420 | enum GNUNET_TRANSPORT_PeerState state; | ||
421 | |||
422 | /** | ||
423 | * Did we sent an KEEP_ALIVE message and are we expecting a response? | ||
424 | */ | ||
425 | int expect_latency_response; | ||
426 | |||
427 | /** | ||
428 | * When a peer wants to connect we have to reply to the 1st SYN message | ||
429 | * with a SYN_ACK message. But sometime we cannot send this message | ||
430 | * immediately since we do not have an address and then we have to remember | ||
431 | * to send this message as soon as we have an address. | ||
432 | * | ||
433 | * Flag to set if we still need to send a SYN_ACK message to the other peer | ||
434 | * (once we have an address to use and the peer has been allowed by our | ||
435 | * blacklist). Initially set to #ACK_UNDEFINED. Set to #ACK_SEND_SYN_ACK | ||
436 | * if we need to send a SYN_ACK. Set to #ACK_SEND_ACK if we did | ||
437 | * send a SYN_ACK and should go to #S_CONNECTED upon receiving a | ||
438 | * 'ACK' (regardless of what our own state machine might say). | ||
439 | */ | ||
440 | enum GST_ACK_State ack_state; | ||
441 | |||
442 | /** | ||
443 | * Tracking utilization of outbound bandwidth | ||
444 | */ | ||
445 | uint32_t util_total_bytes_sent; | ||
446 | |||
447 | /** | ||
448 | * Tracking utilization of inbound bandwidth | ||
449 | */ | ||
450 | uint32_t util_total_bytes_recv; | ||
451 | |||
452 | /** | ||
453 | * Date of last utilization transmission | ||
454 | */ | ||
455 | struct GNUNET_TIME_Absolute last_util_transmission; | ||
456 | }; | ||
457 | |||
458 | |||
459 | /** | ||
460 | * Hash map from peer identities to the respective `struct NeighbourMapEntry`. | ||
461 | */ | ||
462 | static struct GNUNET_CONTAINER_MultiPeerMap *neighbours; | ||
463 | |||
464 | /** | ||
465 | * List of pending blacklist checks: head | ||
466 | */ | ||
467 | static struct BlacklistCheckSwitchContext *pending_bc_head; | ||
468 | |||
469 | /** | ||
470 | * List of pending blacklist checks: tail | ||
471 | */ | ||
472 | static struct BlacklistCheckSwitchContext *pending_bc_tail; | ||
473 | |||
474 | /** | ||
475 | * counter for connected neighbours | ||
476 | */ | ||
477 | static unsigned int neighbours_connected; | ||
478 | |||
479 | /** | ||
480 | * Number of bytes we have currently queued for transmission. | ||
481 | */ | ||
482 | static unsigned long long bytes_in_send_queue; | ||
483 | |||
484 | /** | ||
485 | * Task transmitting utilization data | ||
486 | */ | ||
487 | static struct GNUNET_SCHEDULER_Task *util_transmission_tk; | ||
488 | |||
489 | |||
490 | /** | ||
491 | * Convert the given ACK state to a string. | ||
492 | * | ||
493 | * @param s state | ||
494 | * @return corresponding human-readable string | ||
495 | */ | ||
496 | static char * | ||
497 | print_ack_state (enum GST_ACK_State s) | ||
498 | { | ||
499 | switch (s) | ||
500 | { | ||
501 | case ACK_UNDEFINED: | ||
502 | return "UNDEFINED"; | ||
503 | |||
504 | case ACK_SEND_SYN_ACK: | ||
505 | return "SEND_SYN_ACK"; | ||
506 | |||
507 | case ACK_SEND_ACK: | ||
508 | return "SEND_ACK"; | ||
509 | |||
510 | default: | ||
511 | GNUNET_break (0); | ||
512 | return "N/A"; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | |||
517 | /** | ||
518 | * Send information about a new outbound quota to our clients. | ||
519 | * Note that the outbound quota is enforced client-side (i.e. | ||
520 | * in libgnunettransport). | ||
521 | * | ||
522 | * @param n affected peer | ||
523 | */ | ||
524 | static void | ||
525 | send_outbound_quota_to_clients (struct NeighbourMapEntry *n) | ||
526 | { | ||
527 | struct QuotaSetMessage q_msg; | ||
528 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min; | ||
529 | |||
530 | if (! GNUNET_TRANSPORT_is_connected (n->state)) | ||
531 | return; | ||
532 | #if IGNORE_INBOUND_QUOTA | ||
533 | bandwidth_min = n->primary_address.bandwidth_out; | ||
534 | #else | ||
535 | bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out, | ||
536 | n->neighbour_receive_quota); | ||
537 | #endif | ||
538 | |||
539 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
540 | "Sending outbound quota of %u Bps for peer `%s' to all clients\n", | ||
541 | ntohl (bandwidth_min.value__), | ||
542 | GNUNET_i2s (&n->id)); | ||
543 | q_msg.header.size = htons (sizeof(struct QuotaSetMessage)); | ||
544 | q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA); | ||
545 | q_msg.quota = bandwidth_min; | ||
546 | q_msg.peer = n->id; | ||
547 | GST_clients_broadcast (&q_msg.header, | ||
548 | GNUNET_NO); | ||
549 | } | ||
550 | |||
551 | |||
552 | /** | ||
553 | * Notify our clients that another peer connected to us. | ||
554 | * | ||
555 | * @param n the peer that connected | ||
556 | */ | ||
557 | static void | ||
558 | neighbours_connect_notification (struct NeighbourMapEntry *n) | ||
559 | { | ||
560 | size_t len = sizeof(struct ConnectInfoMessage); | ||
561 | char buf[len] GNUNET_ALIGN; | ||
562 | struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf; | ||
563 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min; | ||
564 | |||
565 | #if IGNORE_INBOUND_QUOTA | ||
566 | bandwidth_min = n->primary_address.bandwidth_out; | ||
567 | #else | ||
568 | bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out, | ||
569 | n->neighbour_receive_quota); | ||
570 | #endif | ||
571 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
572 | "We are now connected to peer `%s'\n", | ||
573 | GNUNET_i2s (&n->id)); | ||
574 | connect_msg->header.size = htons (sizeof(buf)); | ||
575 | connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT); | ||
576 | connect_msg->id = n->id; | ||
577 | connect_msg->quota_out = bandwidth_min; | ||
578 | GST_clients_broadcast (&connect_msg->header, | ||
579 | GNUNET_NO); | ||
580 | } | ||
581 | |||
582 | |||
583 | /** | ||
584 | * Notify our clients (and manipulation) that a peer disconnected from | ||
585 | * us. | ||
586 | * | ||
587 | * @param n the peer that disconnected | ||
588 | */ | ||
589 | static void | ||
590 | neighbours_disconnect_notification (struct NeighbourMapEntry *n) | ||
591 | { | ||
592 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
593 | "Peer `%s' disconnected\n", | ||
594 | GNUNET_i2s (&n->id)); | ||
595 | GST_manipulation_peer_disconnect (&n->id); | ||
596 | GST_clients_broadcast_disconnect (&n->id); | ||
597 | } | ||
598 | |||
599 | |||
600 | /** | ||
601 | * Notify transport clients that a neighbour peer changed its active | ||
602 | * address. | ||
603 | * | ||
604 | * @param peer identity of the peer | ||
605 | * @param address address possibly NULL if peer is not connected | ||
606 | * @param state current state this peer is in | ||
607 | * @param state_timeout timeout for the current state of the peer | ||
608 | * @param bandwidth_in bandwidth assigned inbound, 0 on disconnect | ||
609 | * @param bandwidth_out bandwidth assigned outbound, 0 on disconnect | ||
610 | */ | ||
611 | static void | ||
612 | neighbours_changed_notification (const struct GNUNET_PeerIdentity *peer, | ||
613 | const struct GNUNET_HELLO_Address *address, | ||
614 | enum GNUNET_TRANSPORT_PeerState state, | ||
615 | struct GNUNET_TIME_Absolute state_timeout, | ||
616 | struct GNUNET_BANDWIDTH_Value32NBO | ||
617 | bandwidth_in, | ||
618 | struct GNUNET_BANDWIDTH_Value32NBO | ||
619 | bandwidth_out) | ||
620 | { | ||
621 | (void) bandwidth_in; | ||
622 | (void) bandwidth_out; | ||
623 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
624 | "Notifying about change for peer `%s' with address `%s' in state `%s' timing out at %s\n", | ||
625 | GNUNET_i2s (peer), | ||
626 | GST_plugins_a2s (address), | ||
627 | GNUNET_TRANSPORT_ps2s (state), | ||
628 | GNUNET_STRINGS_absolute_time_to_string (state_timeout)); | ||
629 | /* FIXME: include bandwidth in notification! */ | ||
630 | GST_clients_broadcast_peer_notification (peer, | ||
631 | address, | ||
632 | state, | ||
633 | state_timeout); | ||
634 | } | ||
635 | |||
636 | |||
637 | /** | ||
638 | * Lookup a neighbour entry in the neighbours hash map. | ||
639 | * | ||
640 | * @param pid identity of the peer to look up | ||
641 | * @return the entry, NULL if there is no existing record | ||
642 | */ | ||
643 | static struct NeighbourMapEntry * | ||
644 | lookup_neighbour (const struct GNUNET_PeerIdentity *pid) | ||
645 | { | ||
646 | if (NULL == neighbours) | ||
647 | return NULL; | ||
648 | return GNUNET_CONTAINER_multipeermap_get (neighbours, pid); | ||
649 | } | ||
650 | |||
651 | |||
652 | /** | ||
653 | * Test if we're connected to the given peer. | ||
654 | * | ||
655 | * @param n neighbour entry of peer to test | ||
656 | * @return #GNUNET_YES if we are connected, #GNUNET_NO if not | ||
657 | */ | ||
658 | static int | ||
659 | test_connected (struct NeighbourMapEntry *n) | ||
660 | { | ||
661 | if (NULL == n) | ||
662 | return GNUNET_NO; | ||
663 | return GNUNET_TRANSPORT_is_connected (n->state); | ||
664 | } | ||
665 | |||
666 | |||
667 | /** | ||
668 | * We don't need a given neighbour address any more. | ||
669 | * Release its resources and give appropriate notifications | ||
670 | * to ATS and other subsystems. | ||
671 | * | ||
672 | * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents! | ||
673 | */ | ||
674 | static void | ||
675 | free_address (struct NeighbourAddress *na) | ||
676 | { | ||
677 | if (GNUNET_YES == na->ats_active) | ||
678 | GST_validation_set_address_use (na->address, | ||
679 | GNUNET_NO); | ||
680 | if (NULL != na->address) | ||
681 | { | ||
682 | GST_ats_block_address (na->address, | ||
683 | na->session); | ||
684 | GNUNET_HELLO_address_free (na->address); | ||
685 | na->address = NULL; | ||
686 | } | ||
687 | na->bandwidth_in = GNUNET_BANDWIDTH_value_init (0); | ||
688 | na->bandwidth_out = GNUNET_BANDWIDTH_value_init (0); | ||
689 | na->ats_active = GNUNET_NO; | ||
690 | na->keep_alive_nonce = 0; | ||
691 | na->session = NULL; | ||
692 | } | ||
693 | |||
694 | |||
695 | /** | ||
696 | * Master task run for every neighbour. Performs all of the time-related | ||
697 | * activities (keep alive, send next message, disconnect if idle, finish | ||
698 | * clean up after disconnect). | ||
699 | * | ||
700 | * @param cls the `struct NeighbourMapEntry` for which we are running | ||
701 | */ | ||
702 | static void | ||
703 | master_task (void *cls); | ||
704 | |||
705 | |||
706 | /** | ||
707 | * Set net state and state timeout for this neighbour and notify monitoring | ||
708 | * | ||
709 | * @param n the respective neighbour | ||
710 | * @param s the new state | ||
711 | * @param timeout the new timeout | ||
712 | */ | ||
713 | static void | ||
714 | set_state_and_timeout (struct NeighbourMapEntry *n, | ||
715 | enum GNUNET_TRANSPORT_PeerState s, | ||
716 | struct GNUNET_TIME_Absolute timeout) | ||
717 | { | ||
718 | if (GNUNET_TRANSPORT_is_connected (s) && | ||
719 | (! GNUNET_TRANSPORT_is_connected (n->state))) | ||
720 | { | ||
721 | neighbours_connect_notification (n); | ||
722 | GNUNET_STATISTICS_set (GST_stats, | ||
723 | gettext_noop ("# peers connected"), | ||
724 | ++neighbours_connected, | ||
725 | GNUNET_NO); | ||
726 | } | ||
727 | if ((! GNUNET_TRANSPORT_is_connected (s)) && | ||
728 | GNUNET_TRANSPORT_is_connected (n->state)) | ||
729 | { | ||
730 | GNUNET_STATISTICS_set (GST_stats, | ||
731 | gettext_noop ("# peers connected"), | ||
732 | --neighbours_connected, | ||
733 | GNUNET_NO); | ||
734 | neighbours_disconnect_notification (n); | ||
735 | } | ||
736 | n->state = s; | ||
737 | if ((timeout.abs_value_us < n->timeout.abs_value_us) && | ||
738 | (NULL != n->task)) | ||
739 | { | ||
740 | /* new timeout is earlier, reschedule master task */ | ||
741 | GNUNET_SCHEDULER_cancel (n->task); | ||
742 | n->task = GNUNET_SCHEDULER_add_at (timeout, | ||
743 | &master_task, | ||
744 | n); | ||
745 | } | ||
746 | n->timeout = timeout; | ||
747 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
748 | "Neighbour `%s' changed state to %s with timeout %s\n", | ||
749 | GNUNET_i2s (&n->id), | ||
750 | GNUNET_TRANSPORT_ps2s (s), | ||
751 | GNUNET_STRINGS_absolute_time_to_string (timeout)); | ||
752 | neighbours_changed_notification (&n->id, | ||
753 | n->primary_address.address, | ||
754 | n->state, | ||
755 | n->timeout, | ||
756 | n->primary_address.bandwidth_in, | ||
757 | n->primary_address.bandwidth_out); | ||
758 | } | ||
759 | |||
760 | |||
761 | /** | ||
762 | * Initialize the alternative address of a neighbour | ||
763 | * | ||
764 | * @param n the neighbour | ||
765 | * @param address address of the other peer, NULL if other peer | ||
766 | * connected to us | ||
767 | * @param session session to use (or NULL, in which case an | ||
768 | * address must be setup) | ||
769 | * @param bandwidth_in inbound quota to be used when connection is up | ||
770 | * @param bandwidth_out outbound quota to be used when connection is up | ||
771 | */ | ||
772 | static void | ||
773 | set_alternative_address (struct NeighbourMapEntry *n, | ||
774 | const struct GNUNET_HELLO_Address *address, | ||
775 | struct GNUNET_ATS_Session *session, | ||
776 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, | ||
777 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out) | ||
778 | { | ||
779 | struct GNUNET_TRANSPORT_PluginFunctions *papi; | ||
780 | |||
781 | if (NULL == (papi = GST_plugins_find (address->transport_name))) | ||
782 | { | ||
783 | GNUNET_break (0); | ||
784 | return; | ||
785 | } | ||
786 | if (session == n->alternative_address.session) | ||
787 | { | ||
788 | n->alternative_address.bandwidth_in = bandwidth_in; | ||
789 | n->alternative_address.bandwidth_out = bandwidth_out; | ||
790 | return; | ||
791 | } | ||
792 | if (NULL != n->alternative_address.address) | ||
793 | { | ||
794 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
795 | "Replacing existing alternative address with another one\n"); | ||
796 | free_address (&n->alternative_address); | ||
797 | } | ||
798 | if (NULL == session) | ||
799 | session = papi->get_session (papi->cls, | ||
800 | address); | ||
801 | if (NULL == session) | ||
802 | { | ||
803 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
804 | "Failed to obtain new session for peer `%s' and address '%s'\n", | ||
805 | GNUNET_i2s (&address->peer), | ||
806 | GST_plugins_a2s (address)); | ||
807 | GNUNET_STATISTICS_update (GST_stats, | ||
808 | gettext_noop ("# session creation failed"), | ||
809 | 1, | ||
810 | GNUNET_NO); | ||
811 | return; | ||
812 | } | ||
813 | GST_ats_new_session (address, | ||
814 | session); | ||
815 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
816 | "Neighbour `%s' configured alternative address %s\n", | ||
817 | GNUNET_i2s (&n->id), | ||
818 | GST_plugins_a2s (address)); | ||
819 | |||
820 | n->alternative_address.address = GNUNET_HELLO_address_copy (address); | ||
821 | n->alternative_address.bandwidth_in = bandwidth_in; | ||
822 | n->alternative_address.bandwidth_out = bandwidth_out; | ||
823 | n->alternative_address.session = session; | ||
824 | n->alternative_address.ats_active = GNUNET_NO; | ||
825 | n->alternative_address.keep_alive_nonce = 0; | ||
826 | GNUNET_assert (GNUNET_YES == | ||
827 | GST_ats_is_known (n->alternative_address.address, | ||
828 | n->alternative_address.session)); | ||
829 | } | ||
830 | |||
831 | |||
832 | /** | ||
833 | * Transmit a message using the current session of the given | ||
834 | * neighbour. | ||
835 | * | ||
836 | * @param n entry for the recipient | ||
837 | * @param msgbuf buffer to transmit | ||
838 | * @param msgbuf_size number of bytes in @a msgbuf buffer | ||
839 | * @param priority transmission priority | ||
840 | * @param timeout transmission timeout | ||
841 | * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive | ||
842 | * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise | ||
843 | * @param cont continuation to call when finished (can be NULL) | ||
844 | * @param cont_cls closure for @a cont | ||
845 | * @return timeout (copy of @a timeout or a calculated one if | ||
846 | * @a use_keepalive_timeout is #GNUNET_YES. | ||
847 | */ | ||
848 | static struct GNUNET_TIME_Relative | ||
849 | send_with_session (struct NeighbourMapEntry *n, | ||
850 | const void *msgbuf, | ||
851 | size_t msgbuf_size, | ||
852 | uint32_t priority, | ||
853 | struct GNUNET_TIME_Relative timeout, | ||
854 | unsigned int use_keepalive_timeout, | ||
855 | GNUNET_TRANSPORT_TransmitContinuation cont, | ||
856 | void *cont_cls) | ||
857 | { | ||
858 | struct GNUNET_TRANSPORT_PluginFunctions *papi; | ||
859 | struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL; | ||
860 | |||
861 | GNUNET_assert (NULL != n->primary_address.session); | ||
862 | if ((((NULL == (papi = GST_plugins_find ( | ||
863 | n->primary_address.address->transport_name))) || | ||
864 | (-1 == papi->send (papi->cls, | ||
865 | n->primary_address.session, | ||
866 | msgbuf, | ||
867 | msgbuf_size, | ||
868 | priority, | ||
869 | (result = (GNUNET_NO == use_keepalive_timeout) ? | ||
870 | timeout : | ||
871 | GNUNET_TIME_relative_divide ( | ||
872 | GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, | ||
873 | papi-> | ||
874 | query_keepalive_factor (papi->cls))), | ||
875 | cont, | ||
876 | cont_cls)))) && | ||
877 | (NULL != cont)) | ||
878 | cont (cont_cls, | ||
879 | &n->id, | ||
880 | GNUNET_SYSERR, | ||
881 | msgbuf_size, | ||
882 | 0); | ||
883 | GST_neighbours_notify_data_sent (n->primary_address.address, | ||
884 | n->primary_address.session, | ||
885 | msgbuf_size); | ||
886 | GNUNET_break (NULL != papi); | ||
887 | return result; | ||
888 | } | ||
889 | |||
890 | |||
891 | /** | ||
892 | * Clear the primary address of a neighbour since this address is not | ||
893 | * valid anymore and notify monitoring about it | ||
894 | * | ||
895 | * @param n the neighbour | ||
896 | */ | ||
897 | static void | ||
898 | unset_primary_address (struct NeighbourMapEntry *n) | ||
899 | { | ||
900 | /* Notify monitoring about change */ | ||
901 | if (NULL == n->primary_address.address) | ||
902 | return; | ||
903 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
904 | "Disabling primary address\n"); | ||
905 | neighbours_changed_notification (&n->id, | ||
906 | n->primary_address.address, | ||
907 | n->state, | ||
908 | n->timeout, | ||
909 | GNUNET_BANDWIDTH_value_init (0), | ||
910 | GNUNET_BANDWIDTH_value_init (0)); | ||
911 | free_address (&n->primary_address); | ||
912 | } | ||
913 | |||
914 | |||
915 | /** | ||
916 | * Free a neighbour map entry. | ||
917 | * | ||
918 | * @param n entry to free | ||
919 | */ | ||
920 | static void | ||
921 | free_neighbour (struct NeighbourMapEntry *n) | ||
922 | { | ||
923 | struct MessageQueue *mq; | ||
924 | |||
925 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
926 | "Freeing neighbour state of peer `%s'\n", | ||
927 | GNUNET_i2s (&n->id)); | ||
928 | n->is_active = NULL; /* always free'd by its own continuation! */ | ||
929 | |||
930 | /* fail messages currently in the queue */ | ||
931 | while (NULL != (mq = n->messages_head)) | ||
932 | { | ||
933 | GNUNET_CONTAINER_DLL_remove (n->messages_head, | ||
934 | n->messages_tail, | ||
935 | mq); | ||
936 | if (NULL != mq->cont) | ||
937 | mq->cont (mq->cont_cls, | ||
938 | GNUNET_SYSERR, | ||
939 | mq->message_buf_size, | ||
940 | 0); | ||
941 | GNUNET_free (mq); | ||
942 | } | ||
943 | /* Mark peer as disconnected */ | ||
944 | set_state_and_timeout (n, | ||
945 | GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED, | ||
946 | GNUNET_TIME_UNIT_FOREVER_ABS); | ||
947 | /* free addresses and mark as unused */ | ||
948 | unset_primary_address (n); | ||
949 | |||
950 | if (NULL != n->alternative_address.address) | ||
951 | { | ||
952 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
953 | "Cleaning up alternative address\n"); | ||
954 | free_address (&n->alternative_address); | ||
955 | } | ||
956 | GNUNET_assert (GNUNET_YES == | ||
957 | GNUNET_CONTAINER_multipeermap_remove (neighbours, | ||
958 | &n->id, | ||
959 | n)); | ||
960 | |||
961 | /* Cancel address requests for this peer */ | ||
962 | if (NULL != n->suggest_handle) | ||
963 | { | ||
964 | GNUNET_ATS_connectivity_suggest_cancel (n->suggest_handle); | ||
965 | n->suggest_handle = NULL; | ||
966 | } | ||
967 | |||
968 | /* Cancel the disconnect task */ | ||
969 | if (NULL != n->delayed_disconnect_task) | ||
970 | { | ||
971 | GNUNET_SCHEDULER_cancel (n->delayed_disconnect_task); | ||
972 | n->delayed_disconnect_task = NULL; | ||
973 | } | ||
974 | |||
975 | /* Cancel the master task */ | ||
976 | if (NULL != n->task) | ||
977 | { | ||
978 | GNUNET_SCHEDULER_cancel (n->task); | ||
979 | n->task = NULL; | ||
980 | } | ||
981 | /* free rest of memory */ | ||
982 | GNUNET_free (n); | ||
983 | } | ||
984 | |||
985 | |||
986 | /** | ||
987 | * Function called when the 'DISCONNECT' message has been sent by the | ||
988 | * plugin. Frees the neighbour --- if the entry still exists. | ||
989 | * | ||
990 | * @param cls NULL | ||
991 | * @param target identity of the neighbour that was disconnected | ||
992 | * @param result #GNUNET_OK if the disconnect got out successfully | ||
993 | * @param payload bytes payload | ||
994 | * @param physical bytes on wire | ||
995 | */ | ||
996 | static void | ||
997 | send_disconnect_cont (void *cls, | ||
998 | const struct GNUNET_PeerIdentity *target, | ||
999 | int result, | ||
1000 | size_t payload, | ||
1001 | size_t physical) | ||
1002 | { | ||
1003 | struct NeighbourMapEntry *n; | ||
1004 | |||
1005 | (void) cls; | ||
1006 | (void) result; | ||
1007 | (void) payload; | ||
1008 | (void) physical; | ||
1009 | n = lookup_neighbour (target); | ||
1010 | if (NULL == n) | ||
1011 | return; /* already gone */ | ||
1012 | if (GNUNET_TRANSPORT_PS_DISCONNECT != n->state) | ||
1013 | return; /* have created a fresh entry since */ | ||
1014 | if (NULL != n->task) | ||
1015 | GNUNET_SCHEDULER_cancel (n->task); | ||
1016 | n->task = GNUNET_SCHEDULER_add_now (&master_task, n); | ||
1017 | } | ||
1018 | |||
1019 | |||
1020 | /** | ||
1021 | * Transmit a DISCONNECT message to the other peer. | ||
1022 | * | ||
1023 | * @param n neighbour to send DISCONNECT message. | ||
1024 | */ | ||
1025 | static void | ||
1026 | send_disconnect (struct NeighbourMapEntry *n) | ||
1027 | { | ||
1028 | struct GNUNET_ATS_SessionDisconnectMessage disconnect_msg; | ||
1029 | |||
1030 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1031 | "Sending DISCONNECT message to peer `%4s'\n", | ||
1032 | GNUNET_i2s (&n->id)); | ||
1033 | disconnect_msg.header.size = htons (sizeof(struct | ||
1034 | GNUNET_ATS_SessionDisconnectMessage)); | ||
1035 | disconnect_msg.header.type = | ||
1036 | htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT); | ||
1037 | disconnect_msg.reserved = htonl (0); | ||
1038 | disconnect_msg.purpose.size = | ||
1039 | htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) | ||
1040 | + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) | ||
1041 | + sizeof(struct GNUNET_TIME_AbsoluteNBO)); | ||
1042 | disconnect_msg.purpose.purpose = | ||
1043 | htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT); | ||
1044 | disconnect_msg.timestamp = | ||
1045 | GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); | ||
1046 | disconnect_msg.public_key = GST_my_identity.public_key; | ||
1047 | GNUNET_assert (GNUNET_OK == | ||
1048 | GNUNET_CRYPTO_eddsa_sign_ (&GST_my_private_key, | ||
1049 | &disconnect_msg.purpose, | ||
1050 | &disconnect_msg.signature)); | ||
1051 | |||
1052 | (void) send_with_session (n, | ||
1053 | &disconnect_msg, | ||
1054 | sizeof(disconnect_msg), | ||
1055 | UINT32_MAX, | ||
1056 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
1057 | GNUNET_NO, | ||
1058 | &send_disconnect_cont, | ||
1059 | NULL); | ||
1060 | GNUNET_STATISTICS_update (GST_stats, | ||
1061 | gettext_noop ("# DISCONNECT messages sent"), | ||
1062 | 1, | ||
1063 | GNUNET_NO); | ||
1064 | } | ||
1065 | |||
1066 | |||
1067 | /** | ||
1068 | * Disconnect from the given neighbour, clean up the record. | ||
1069 | * | ||
1070 | * @param n neighbour to disconnect from | ||
1071 | */ | ||
1072 | static void | ||
1073 | disconnect_neighbour (struct NeighbourMapEntry *n) | ||
1074 | { | ||
1075 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1076 | "Disconnecting from peer %s in state %s\n", | ||
1077 | GNUNET_i2s (&n->id), | ||
1078 | GNUNET_TRANSPORT_ps2s (n->state)); | ||
1079 | /* depending on state, notify neighbour and/or upper layers of this peer | ||
1080 | about disconnect */ | ||
1081 | switch (n->state) | ||
1082 | { | ||
1083 | case GNUNET_TRANSPORT_PS_NOT_CONNECTED: | ||
1084 | case GNUNET_TRANSPORT_PS_INIT_ATS: | ||
1085 | /* other peer is completely unaware of us, no need to send DISCONNECT */ | ||
1086 | free_neighbour (n); | ||
1087 | return; | ||
1088 | |||
1089 | case GNUNET_TRANSPORT_PS_SYN_SENT: | ||
1090 | send_disconnect (n); | ||
1091 | set_state_and_timeout (n, | ||
1092 | GNUNET_TRANSPORT_PS_DISCONNECT, | ||
1093 | GNUNET_TIME_UNIT_FOREVER_ABS); | ||
1094 | break; | ||
1095 | |||
1096 | case GNUNET_TRANSPORT_PS_SYN_RECV_ATS: | ||
1097 | /* we never ACK'ed the other peer's request, no need to send DISCONNECT */ | ||
1098 | free_neighbour (n); | ||
1099 | return; | ||
1100 | |||
1101 | case GNUNET_TRANSPORT_PS_SYN_RECV_ACK: | ||
1102 | /* we DID ACK the other peer's request, must send DISCONNECT */ | ||
1103 | send_disconnect (n); | ||
1104 | set_state_and_timeout (n, | ||
1105 | GNUNET_TRANSPORT_PS_DISCONNECT, | ||
1106 | GNUNET_TIME_UNIT_FOREVER_ABS); | ||
1107 | break; | ||
1108 | |||
1109 | case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: | ||
1110 | case GNUNET_TRANSPORT_PS_CONNECTED: | ||
1111 | case GNUNET_TRANSPORT_PS_RECONNECT_SENT: | ||
1112 | /* we are currently connected, need to send disconnect and do | ||
1113 | internal notifications and update statistics */ | ||
1114 | send_disconnect (n); | ||
1115 | set_state_and_timeout (n, | ||
1116 | GNUNET_TRANSPORT_PS_DISCONNECT, | ||
1117 | GNUNET_TIME_UNIT_FOREVER_ABS); | ||
1118 | break; | ||
1119 | |||
1120 | case GNUNET_TRANSPORT_PS_RECONNECT_ATS: | ||
1121 | /* Disconnecting while waiting for an ATS address to reconnect, | ||
1122 | * cannot send DISCONNECT */ | ||
1123 | free_neighbour (n); | ||
1124 | return; | ||
1125 | |||
1126 | case GNUNET_TRANSPORT_PS_DISCONNECT: | ||
1127 | /* already disconnected, ignore */ | ||
1128 | break; | ||
1129 | |||
1130 | case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED: | ||
1131 | /* already cleaned up, how did we get here!? */ | ||
1132 | GNUNET_assert (0); | ||
1133 | break; | ||
1134 | |||
1135 | default: | ||
1136 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1137 | "Unhandled state `%s'\n", | ||
1138 | GNUNET_TRANSPORT_ps2s (n->state)); | ||
1139 | GNUNET_break (0); | ||
1140 | break; | ||
1141 | } | ||
1142 | /* schedule timeout to clean up */ | ||
1143 | if (NULL != n->task) | ||
1144 | GNUNET_SCHEDULER_cancel (n->task); | ||
1145 | n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT, | ||
1146 | &master_task, | ||
1147 | n); | ||
1148 | } | ||
1149 | |||
1150 | |||
1151 | /** | ||
1152 | * Change the incoming quota for the given peer. Updates | ||
1153 | * our own receive rate and informs the neighbour about | ||
1154 | * the new quota. | ||
1155 | * | ||
1156 | * @param n neighbour entry to change quota for | ||
1157 | * @param quota new quota | ||
1158 | * @return #GNUNET_YES if @a n is still valid, #GNUNET_NO if | ||
1159 | * @a n was freed | ||
1160 | */ | ||
1161 | static int | ||
1162 | set_incoming_quota (struct NeighbourMapEntry *n, | ||
1163 | struct GNUNET_BANDWIDTH_Value32NBO quota) | ||
1164 | { | ||
1165 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1166 | "Setting inbound quota of %u Bps for peer `%s' to all clients\n", | ||
1167 | ntohl (quota.value__), GNUNET_i2s (&n->id)); | ||
1168 | GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, | ||
1169 | quota); | ||
1170 | if (0 != ntohl (quota.value__)) | ||
1171 | { | ||
1172 | struct GNUNET_ATS_SessionQuotaMessage sqm; | ||
1173 | |||
1174 | sqm.header.size = htons (sizeof(struct GNUNET_ATS_SessionQuotaMessage)); | ||
1175 | sqm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA); | ||
1176 | sqm.quota = quota.value__; | ||
1177 | if (NULL != n->primary_address.session) | ||
1178 | (void) send_with_session (n, | ||
1179 | &sqm, | ||
1180 | sizeof(sqm), | ||
1181 | UINT32_MAX - 1, | ||
1182 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
1183 | GNUNET_NO, | ||
1184 | NULL, NULL); | ||
1185 | return GNUNET_YES; | ||
1186 | } | ||
1187 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1188 | "Disconnecting peer `%s' due to SET_QUOTA\n", | ||
1189 | GNUNET_i2s (&n->id)); | ||
1190 | if (GNUNET_YES == test_connected (n)) | ||
1191 | GNUNET_STATISTICS_update (GST_stats, | ||
1192 | gettext_noop ("# disconnects due to quota of 0"), | ||
1193 | 1, GNUNET_NO); | ||
1194 | disconnect_neighbour (n); | ||
1195 | return GNUNET_NO; | ||
1196 | } | ||
1197 | |||
1198 | |||
1199 | /** | ||
1200 | * Initialize the primary address of a neighbour | ||
1201 | * | ||
1202 | * @param n the neighbour | ||
1203 | * @param address address of the other peer, NULL if other peer | ||
1204 | * connected to us | ||
1205 | * @param session session to use (or NULL, in which case an | ||
1206 | * address must be setup) | ||
1207 | * @param bandwidth_in inbound quota to be used when connection is up | ||
1208 | * @param bandwidth_out outbound quota to be used when connection is up | ||
1209 | */ | ||
1210 | static void | ||
1211 | set_primary_address (struct NeighbourMapEntry *n, | ||
1212 | const struct GNUNET_HELLO_Address *address, | ||
1213 | struct GNUNET_ATS_Session *session, | ||
1214 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, | ||
1215 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out) | ||
1216 | { | ||
1217 | if (session == n->primary_address.session) | ||
1218 | { | ||
1219 | GST_validation_set_address_use (n->primary_address.address, | ||
1220 | GNUNET_YES); | ||
1221 | if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__) | ||
1222 | { | ||
1223 | n->primary_address.bandwidth_in = bandwidth_in; | ||
1224 | if (GNUNET_YES != | ||
1225 | set_incoming_quota (n, | ||
1226 | bandwidth_in)) | ||
1227 | return; | ||
1228 | } | ||
1229 | if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__) | ||
1230 | { | ||
1231 | n->primary_address.bandwidth_out = bandwidth_out; | ||
1232 | send_outbound_quota_to_clients (n); | ||
1233 | } | ||
1234 | return; | ||
1235 | } | ||
1236 | if ((NULL != n->primary_address.address) && | ||
1237 | (0 == GNUNET_HELLO_address_cmp (address, | ||
1238 | n->primary_address.address))) | ||
1239 | { | ||
1240 | GNUNET_break (0); | ||
1241 | return; | ||
1242 | } | ||
1243 | if (NULL == session) | ||
1244 | { | ||
1245 | GNUNET_break (0); | ||
1246 | GST_ats_block_address (address, | ||
1247 | session); | ||
1248 | return; | ||
1249 | } | ||
1250 | if (NULL != n->primary_address.address) | ||
1251 | { | ||
1252 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1253 | "Replacing existing primary address with another one\n"); | ||
1254 | free_address (&n->primary_address); | ||
1255 | } | ||
1256 | n->primary_address.address = GNUNET_HELLO_address_copy (address); | ||
1257 | n->primary_address.bandwidth_in = bandwidth_in; | ||
1258 | n->primary_address.bandwidth_out = bandwidth_out; | ||
1259 | n->primary_address.session = session; | ||
1260 | n->primary_address.keep_alive_nonce = 0; | ||
1261 | GNUNET_assert (GNUNET_YES == | ||
1262 | GST_ats_is_known (n->primary_address.address, | ||
1263 | n->primary_address.session)); | ||
1264 | /* subsystems about address use */ | ||
1265 | GST_validation_set_address_use (n->primary_address.address, | ||
1266 | GNUNET_YES); | ||
1267 | if (GNUNET_YES != | ||
1268 | set_incoming_quota (n, | ||
1269 | bandwidth_in)) | ||
1270 | return; | ||
1271 | send_outbound_quota_to_clients (n); | ||
1272 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1273 | "Neighbour `%s' switched to address `%s'\n", | ||
1274 | GNUNET_i2s (&n->id), | ||
1275 | GST_plugins_a2s (address)); | ||
1276 | |||
1277 | neighbours_changed_notification (&n->id, | ||
1278 | n->primary_address.address, | ||
1279 | n->state, | ||
1280 | n->timeout, | ||
1281 | n->primary_address.bandwidth_in, | ||
1282 | n->primary_address.bandwidth_out); | ||
1283 | } | ||
1284 | |||
1285 | |||
1286 | /** | ||
1287 | * We're done with our transmission attempt, continue processing. | ||
1288 | * | ||
1289 | * @param cls the `struct MessageQueue` of the message | ||
1290 | * @param receiver intended receiver | ||
1291 | * @param success whether it worked or not | ||
1292 | * @param size_payload bytes payload sent | ||
1293 | * @param physical bytes sent on wire | ||
1294 | */ | ||
1295 | static void | ||
1296 | transmit_send_continuation (void *cls, | ||
1297 | const struct GNUNET_PeerIdentity *receiver, | ||
1298 | int success, | ||
1299 | size_t size_payload, | ||
1300 | size_t physical) | ||
1301 | { | ||
1302 | struct MessageQueue *mq = cls; | ||
1303 | struct NeighbourMapEntry *n; | ||
1304 | |||
1305 | if (NULL == (n = lookup_neighbour (receiver))) | ||
1306 | { | ||
1307 | if (NULL != mq->cont) | ||
1308 | mq->cont (mq->cont_cls, | ||
1309 | GNUNET_SYSERR /* not connected */, | ||
1310 | size_payload, | ||
1311 | 0); | ||
1312 | GNUNET_free (mq); | ||
1313 | return; /* disconnect or other error while transmitting, can happen */ | ||
1314 | } | ||
1315 | if (n->is_active == mq) | ||
1316 | { | ||
1317 | /* this is still "our" neighbour, remove us from its queue | ||
1318 | and allow it to send the next message now */ | ||
1319 | n->is_active = NULL; | ||
1320 | if (NULL != n->task) | ||
1321 | GNUNET_SCHEDULER_cancel (n->task); | ||
1322 | n->task = GNUNET_SCHEDULER_add_now (&master_task, | ||
1323 | n); | ||
1324 | } | ||
1325 | if (bytes_in_send_queue < mq->message_buf_size) | ||
1326 | { | ||
1327 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1328 | "Bytes_in_send_queue `%llu', Message_size %u, result: %s, payload %u, on wire %u\n", | ||
1329 | bytes_in_send_queue, | ||
1330 | (unsigned int) mq->message_buf_size, | ||
1331 | (GNUNET_OK == success) ? "OK" : "FAIL", | ||
1332 | (unsigned int) size_payload, | ||
1333 | (unsigned int) physical); | ||
1334 | GNUNET_break (0); | ||
1335 | } | ||
1336 | |||
1337 | GNUNET_break (size_payload == mq->message_buf_size); | ||
1338 | bytes_in_send_queue -= mq->message_buf_size; | ||
1339 | GNUNET_STATISTICS_set (GST_stats, | ||
1340 | gettext_noop ( | ||
1341 | "# bytes in message queue for other peers"), | ||
1342 | bytes_in_send_queue, | ||
1343 | GNUNET_NO); | ||
1344 | if (GNUNET_OK == success) | ||
1345 | GNUNET_STATISTICS_update (GST_stats, | ||
1346 | gettext_noop ( | ||
1347 | "# messages transmitted to other peers"), | ||
1348 | 1, | ||
1349 | GNUNET_NO); | ||
1350 | else | ||
1351 | GNUNET_STATISTICS_update (GST_stats, | ||
1352 | gettext_noop | ||
1353 | ( | ||
1354 | "# transmission failures for messages to other peers"), | ||
1355 | 1, GNUNET_NO); | ||
1356 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1357 | "Sending message to `%s' of type %u with %u bytes was a %s\n", | ||
1358 | GNUNET_i2s (receiver), | ||
1359 | ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type), | ||
1360 | (unsigned int) mq->message_buf_size, | ||
1361 | (success == GNUNET_OK) ? "success" : "FAILURE"); | ||
1362 | if (NULL != mq->cont) | ||
1363 | mq->cont (mq->cont_cls, | ||
1364 | success, | ||
1365 | size_payload, | ||
1366 | physical); | ||
1367 | GNUNET_free (mq); | ||
1368 | } | ||
1369 | |||
1370 | |||
1371 | /** | ||
1372 | * Check the message list for the given neighbour and if we can | ||
1373 | * send a message, do so. This function should only be called | ||
1374 | * if the connection is at least generally ready for transmission. | ||
1375 | * While we will only send one message at a time, no bandwidth | ||
1376 | * quota management is performed here. If a message was given to | ||
1377 | * the plugin, the continuation will automatically re-schedule | ||
1378 | * the 'master' task once the next message might be transmitted. | ||
1379 | * | ||
1380 | * @param n target peer for which to transmit | ||
1381 | */ | ||
1382 | static void | ||
1383 | try_transmission_to_peer (struct NeighbourMapEntry *n) | ||
1384 | { | ||
1385 | struct MessageQueue *mq; | ||
1386 | struct GNUNET_TIME_Relative timeout; | ||
1387 | |||
1388 | if (NULL == n->primary_address.address) | ||
1389 | { | ||
1390 | /* no address, why are we here? */ | ||
1391 | GNUNET_break (0); | ||
1392 | return; | ||
1393 | } | ||
1394 | if ((0 == n->primary_address.address->address_length) && | ||
1395 | (NULL == n->primary_address.session)) | ||
1396 | { | ||
1397 | /* no address, why are we here? */ | ||
1398 | GNUNET_break (0); | ||
1399 | return; | ||
1400 | } | ||
1401 | if (NULL != n->is_active) | ||
1402 | { | ||
1403 | /* transmission already pending */ | ||
1404 | return; | ||
1405 | } | ||
1406 | |||
1407 | /* timeout messages from the queue that are past their due date */ | ||
1408 | while (NULL != (mq = n->messages_head)) | ||
1409 | { | ||
1410 | timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout); | ||
1411 | if (timeout.rel_value_us > 0) | ||
1412 | break; | ||
1413 | GNUNET_STATISTICS_update (GST_stats, | ||
1414 | gettext_noop ( | ||
1415 | "# messages timed out while in transport queue"), | ||
1416 | 1, | ||
1417 | GNUNET_NO); | ||
1418 | GNUNET_CONTAINER_DLL_remove (n->messages_head, | ||
1419 | n->messages_tail, | ||
1420 | mq); | ||
1421 | n->is_active = mq; | ||
1422 | transmit_send_continuation (mq, | ||
1423 | &n->id, | ||
1424 | GNUNET_SYSERR, | ||
1425 | mq->message_buf_size, | ||
1426 | 0); /* timeout */ | ||
1427 | } | ||
1428 | if (NULL == mq) | ||
1429 | return; /* no more messages */ | ||
1430 | if (NULL == n->primary_address.address) | ||
1431 | { | ||
1432 | /* transmit_send_continuation() caused us to drop session, | ||
1433 | can't try transmission anymore. */ | ||
1434 | return; | ||
1435 | } | ||
1436 | |||
1437 | |||
1438 | GNUNET_CONTAINER_DLL_remove (n->messages_head, | ||
1439 | n->messages_tail, | ||
1440 | mq); | ||
1441 | n->is_active = mq; | ||
1442 | |||
1443 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1444 | "Giving message with %u bytes to plugin session %p\n", | ||
1445 | (unsigned int) mq->message_buf_size, | ||
1446 | n->primary_address.session); | ||
1447 | (void) send_with_session (n, | ||
1448 | mq->message_buf, | ||
1449 | mq->message_buf_size, | ||
1450 | 0 /* priority */, | ||
1451 | timeout, | ||
1452 | GNUNET_NO, | ||
1453 | &transmit_send_continuation, | ||
1454 | mq); | ||
1455 | } | ||
1456 | |||
1457 | |||
1458 | /** | ||
1459 | * Send keepalive message to the neighbour. Must only be called | ||
1460 | * if we are on 'connected' state or while trying to switch addresses. | ||
1461 | * Will internally determine if a keepalive is truly needed (so can | ||
1462 | * always be called). | ||
1463 | * | ||
1464 | * @param n neighbour that went idle and needs a keepalive | ||
1465 | */ | ||
1466 | static void | ||
1467 | send_keepalive (struct NeighbourMapEntry *n) | ||
1468 | { | ||
1469 | struct GNUNET_ATS_SessionKeepAliveMessage m; | ||
1470 | struct GNUNET_TIME_Relative timeout; | ||
1471 | uint32_t nonce; | ||
1472 | |||
1473 | GNUNET_assert ((GNUNET_TRANSPORT_PS_CONNECTED == n->state) || | ||
1474 | (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)); | ||
1475 | if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0) | ||
1476 | return; /* no keepalive needed at this time */ | ||
1477 | |||
1478 | nonce = 0; /* 0 indicates 'not set' */ | ||
1479 | while (0 == nonce) | ||
1480 | nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, | ||
1481 | UINT32_MAX); | ||
1482 | |||
1483 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1484 | "Sending KEEPALIVE to peer `%s' with nonce %u\n", | ||
1485 | GNUNET_i2s (&n->id), | ||
1486 | nonce); | ||
1487 | m.header.size = htons (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage)); | ||
1488 | m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE); | ||
1489 | m.nonce = htonl (nonce); | ||
1490 | |||
1491 | timeout = send_with_session (n, | ||
1492 | &m, | ||
1493 | sizeof(m), | ||
1494 | UINT32_MAX /* priority */, | ||
1495 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
1496 | GNUNET_YES, | ||
1497 | NULL, NULL); | ||
1498 | GNUNET_STATISTICS_update (GST_stats, | ||
1499 | gettext_noop ("# KEEPALIVES sent"), | ||
1500 | 1, | ||
1501 | GNUNET_NO); | ||
1502 | n->primary_address.keep_alive_nonce = nonce; | ||
1503 | n->expect_latency_response = GNUNET_YES; | ||
1504 | n->last_keep_alive_time = GNUNET_TIME_absolute_get (); | ||
1505 | n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout); | ||
1506 | } | ||
1507 | |||
1508 | |||
1509 | /** | ||
1510 | * Keep the connection to the given neighbour alive longer, | ||
1511 | * we received a KEEPALIVE (or equivalent); send a response. | ||
1512 | * | ||
1513 | * @param neighbour neighbour to keep alive (by sending keep alive response) | ||
1514 | * @param m the keep alive message containing the nonce to respond to | ||
1515 | */ | ||
1516 | void | ||
1517 | GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour, | ||
1518 | const struct GNUNET_MessageHeader *m) | ||
1519 | { | ||
1520 | struct NeighbourMapEntry *n; | ||
1521 | const struct GNUNET_ATS_SessionKeepAliveMessage *msg_in; | ||
1522 | struct GNUNET_ATS_SessionKeepAliveMessage msg; | ||
1523 | |||
1524 | if (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size)) | ||
1525 | { | ||
1526 | GNUNET_break_op (0); | ||
1527 | return; | ||
1528 | } | ||
1529 | |||
1530 | msg_in = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m; | ||
1531 | if (NULL == (n = lookup_neighbour (neighbour))) | ||
1532 | { | ||
1533 | GNUNET_STATISTICS_update (GST_stats, | ||
1534 | gettext_noop | ||
1535 | ("# KEEPALIVE messages discarded (peer unknown)"), | ||
1536 | 1, GNUNET_NO); | ||
1537 | return; | ||
1538 | } | ||
1539 | if (NULL == n->primary_address.session) | ||
1540 | { | ||
1541 | GNUNET_STATISTICS_update (GST_stats, | ||
1542 | gettext_noop | ||
1543 | ("# KEEPALIVE messages discarded (no session)"), | ||
1544 | 1, GNUNET_NO); | ||
1545 | return; | ||
1546 | } | ||
1547 | |||
1548 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1549 | "Received KEEPALIVE request from peer `%s' with nonce %u\n", | ||
1550 | GNUNET_i2s (&n->id), | ||
1551 | ntohl (msg_in->nonce)); | ||
1552 | GNUNET_STATISTICS_update (GST_stats, | ||
1553 | gettext_noop ( | ||
1554 | "# KEEPALIVES received in good order"), | ||
1555 | 1, | ||
1556 | GNUNET_NO); | ||
1557 | |||
1558 | /* send reply to allow neighbour to measure latency */ | ||
1559 | msg.header.size = htons (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage)); | ||
1560 | msg.header.type = htons ( | ||
1561 | GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE); | ||
1562 | msg.nonce = msg_in->nonce; | ||
1563 | (void) send_with_session (n, | ||
1564 | &msg, | ||
1565 | sizeof(struct GNUNET_ATS_SessionKeepAliveMessage), | ||
1566 | UINT32_MAX /* priority */, | ||
1567 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
1568 | GNUNET_YES, | ||
1569 | NULL, NULL); | ||
1570 | } | ||
1571 | |||
1572 | |||
1573 | /** | ||
1574 | * We received a KEEP_ALIVE_RESPONSE message and use this to calculate | ||
1575 | * latency to this peer. Pass the updated information (existing ats | ||
1576 | * plus calculated latency) to ATS. | ||
1577 | * | ||
1578 | * @param neighbour neighbour to keep alive | ||
1579 | * @param m the message containing the keep alive response | ||
1580 | */ | ||
1581 | void | ||
1582 | GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour, | ||
1583 | const struct GNUNET_MessageHeader *m) | ||
1584 | { | ||
1585 | struct NeighbourMapEntry *n; | ||
1586 | const struct GNUNET_ATS_SessionKeepAliveMessage *msg; | ||
1587 | struct GNUNET_TRANSPORT_PluginFunctions *papi; | ||
1588 | struct GNUNET_TIME_Relative latency; | ||
1589 | |||
1590 | if (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size)) | ||
1591 | { | ||
1592 | GNUNET_break_op (0); | ||
1593 | return; | ||
1594 | } | ||
1595 | |||
1596 | msg = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m; | ||
1597 | if (NULL == (n = lookup_neighbour (neighbour))) | ||
1598 | { | ||
1599 | GNUNET_STATISTICS_update (GST_stats, | ||
1600 | gettext_noop ( | ||
1601 | "# KEEPALIVE_RESPONSEs discarded (not connected)"), | ||
1602 | 1, | ||
1603 | GNUNET_NO); | ||
1604 | return; | ||
1605 | } | ||
1606 | if ((GNUNET_TRANSPORT_PS_CONNECTED != n->state) || | ||
1607 | (GNUNET_YES != n->expect_latency_response)) | ||
1608 | { | ||
1609 | GNUNET_STATISTICS_update (GST_stats, | ||
1610 | gettext_noop ( | ||
1611 | "# KEEPALIVE_RESPONSEs discarded (not expected)"), | ||
1612 | 1, | ||
1613 | GNUNET_NO); | ||
1614 | return; | ||
1615 | } | ||
1616 | if (NULL == n->primary_address.address) | ||
1617 | { | ||
1618 | GNUNET_STATISTICS_update (GST_stats, | ||
1619 | gettext_noop ( | ||
1620 | "# KEEPALIVE_RESPONSEs discarded (address changed)"), | ||
1621 | 1, | ||
1622 | GNUNET_NO); | ||
1623 | return; | ||
1624 | } | ||
1625 | if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce)) | ||
1626 | { | ||
1627 | if (0 == n->primary_address.keep_alive_nonce) | ||
1628 | GNUNET_STATISTICS_update (GST_stats, | ||
1629 | gettext_noop ( | ||
1630 | "# KEEPALIVE_RESPONSEs discarded (no nonce)"), | ||
1631 | 1, | ||
1632 | GNUNET_NO); | ||
1633 | else | ||
1634 | GNUNET_STATISTICS_update (GST_stats, | ||
1635 | gettext_noop ( | ||
1636 | "# KEEPALIVE_RESPONSEs discarded (bad nonce)"), | ||
1637 | 1, | ||
1638 | GNUNET_NO); | ||
1639 | return; | ||
1640 | } | ||
1641 | GNUNET_STATISTICS_update (GST_stats, | ||
1642 | gettext_noop ( | ||
1643 | "# KEEPALIVE_RESPONSEs received (OK)"), | ||
1644 | 1, | ||
1645 | GNUNET_NO); | ||
1646 | |||
1647 | |||
1648 | /* Update session timeout here */ | ||
1649 | if (NULL != (papi = GST_plugins_find ( | ||
1650 | n->primary_address.address->transport_name))) | ||
1651 | { | ||
1652 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1653 | "Updating session for peer `%s' for session %p\n", | ||
1654 | GNUNET_i2s (&n->id), | ||
1655 | n->primary_address.session); | ||
1656 | papi->update_session_timeout (papi->cls, | ||
1657 | &n->id, | ||
1658 | n->primary_address.session); | ||
1659 | } | ||
1660 | else | ||
1661 | { | ||
1662 | GNUNET_break (0); | ||
1663 | } | ||
1664 | |||
1665 | n->primary_address.keep_alive_nonce = 0; | ||
1666 | n->expect_latency_response = GNUNET_NO; | ||
1667 | set_state_and_timeout (n, | ||
1668 | n->state, | ||
1669 | GNUNET_TIME_relative_to_absolute ( | ||
1670 | GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT)); | ||
1671 | |||
1672 | latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time); | ||
1673 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1674 | "Received KEEPALIVE_RESPONSE from peer `%s', latency is %s\n", | ||
1675 | GNUNET_i2s (&n->id), | ||
1676 | GNUNET_STRINGS_relative_time_to_string (latency, | ||
1677 | GNUNET_YES)); | ||
1678 | GST_ats_update_delay (n->primary_address.address, | ||
1679 | GNUNET_TIME_relative_divide (latency, | ||
1680 | 2)); | ||
1681 | } | ||
1682 | |||
1683 | |||
1684 | /** | ||
1685 | * We have received a message from the given sender. How long should | ||
1686 | * we delay before receiving more? (Also used to keep the peer marked | ||
1687 | * as live). | ||
1688 | * | ||
1689 | * @param sender sender of the message | ||
1690 | * @param size size of the message | ||
1691 | * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients | ||
1692 | * #GNUNET_NO if the neighbour is not connected or violates the quota, | ||
1693 | * #GNUNET_SYSERR if the connection is not fully up yet | ||
1694 | * @return how long to wait before reading more from this sender | ||
1695 | */ | ||
1696 | struct GNUNET_TIME_Relative | ||
1697 | GST_neighbours_calculate_receive_delay (const struct | ||
1698 | GNUNET_PeerIdentity *sender, | ||
1699 | ssize_t size, | ||
1700 | int *do_forward) | ||
1701 | { | ||
1702 | struct NeighbourMapEntry *n; | ||
1703 | struct GNUNET_TIME_Relative ret; | ||
1704 | |||
1705 | if (NULL == neighbours) | ||
1706 | { | ||
1707 | *do_forward = GNUNET_NO; | ||
1708 | return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */ | ||
1709 | } | ||
1710 | if (NULL == (n = lookup_neighbour (sender))) | ||
1711 | { | ||
1712 | GNUNET_STATISTICS_update (GST_stats, | ||
1713 | gettext_noop ( | ||
1714 | "# messages discarded due to lack of neighbour record"), | ||
1715 | 1, | ||
1716 | GNUNET_NO); | ||
1717 | *do_forward = GNUNET_NO; | ||
1718 | return GNUNET_TIME_UNIT_ZERO; | ||
1719 | } | ||
1720 | if (! test_connected (n)) | ||
1721 | { | ||
1722 | *do_forward = GNUNET_SYSERR; | ||
1723 | return GNUNET_TIME_UNIT_ZERO; | ||
1724 | } | ||
1725 | if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size)) | ||
1726 | { | ||
1727 | n->quota_violation_count++; | ||
1728 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1729 | "Bandwidth quota (%u b/s) violation detected (total of %u).\n", | ||
1730 | n->in_tracker.available_bytes_per_s__, | ||
1731 | n->quota_violation_count); | ||
1732 | /* Discount 32k per violation */ | ||
1733 | GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024); | ||
1734 | } | ||
1735 | else | ||
1736 | { | ||
1737 | if (n->quota_violation_count > 0) | ||
1738 | { | ||
1739 | /* try to add 32k back */ | ||
1740 | GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024); | ||
1741 | n->quota_violation_count--; | ||
1742 | } | ||
1743 | } | ||
1744 | if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD) | ||
1745 | { | ||
1746 | GNUNET_STATISTICS_update (GST_stats, | ||
1747 | gettext_noop | ||
1748 | ("# bandwidth quota violations by other peers"), | ||
1749 | 1, GNUNET_NO); | ||
1750 | *do_forward = GNUNET_NO; | ||
1751 | return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT; | ||
1752 | } | ||
1753 | *do_forward = GNUNET_YES; | ||
1754 | ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024); | ||
1755 | if (ret.rel_value_us > 0) | ||
1756 | { | ||
1757 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1758 | "Throttling read (%lld bytes excess at %u b/s), waiting %s before reading more.\n", | ||
1759 | (long long) n->in_tracker.consumption_since_last_update__, | ||
1760 | (unsigned int) n->in_tracker.available_bytes_per_s__, | ||
1761 | GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES)); | ||
1762 | GNUNET_STATISTICS_update (GST_stats, | ||
1763 | gettext_noop ("# ms throttling suggested"), | ||
1764 | (int64_t) ret.rel_value_us / 1000LL, | ||
1765 | GNUNET_NO); | ||
1766 | } | ||
1767 | return ret; | ||
1768 | } | ||
1769 | |||
1770 | |||
1771 | /** | ||
1772 | * Transmit a message to the given target using the active connection. | ||
1773 | * | ||
1774 | * @param target destination | ||
1775 | * @param msg message to send | ||
1776 | * @param msg_size number of bytes in msg | ||
1777 | * @param timeout when to fail with timeout | ||
1778 | * @param cont function to call when done | ||
1779 | * @param cont_cls closure for @a cont | ||
1780 | */ | ||
1781 | void | ||
1782 | GST_neighbours_send (const struct GNUNET_PeerIdentity *target, | ||
1783 | const void *msg, | ||
1784 | size_t msg_size, | ||
1785 | struct GNUNET_TIME_Relative timeout, | ||
1786 | GST_NeighbourSendContinuation cont, | ||
1787 | void *cont_cls) | ||
1788 | { | ||
1789 | struct NeighbourMapEntry *n; | ||
1790 | struct MessageQueue *mq; | ||
1791 | |||
1792 | /* All ove these cases should never happen; they are all API violations. | ||
1793 | But we check anyway, just to be sure. */ | ||
1794 | if (NULL == (n = lookup_neighbour (target))) | ||
1795 | { | ||
1796 | GNUNET_break (0); | ||
1797 | if (NULL != cont) | ||
1798 | cont (cont_cls, | ||
1799 | GNUNET_SYSERR, | ||
1800 | msg_size, | ||
1801 | 0); | ||
1802 | return; | ||
1803 | } | ||
1804 | if (GNUNET_YES != test_connected (n)) | ||
1805 | { | ||
1806 | GNUNET_break (0); | ||
1807 | if (NULL != cont) | ||
1808 | cont (cont_cls, | ||
1809 | GNUNET_SYSERR, | ||
1810 | msg_size, | ||
1811 | 0); | ||
1812 | return; | ||
1813 | } | ||
1814 | bytes_in_send_queue += msg_size; | ||
1815 | GNUNET_STATISTICS_set (GST_stats, | ||
1816 | gettext_noop | ||
1817 | ("# bytes in message queue for other peers"), | ||
1818 | bytes_in_send_queue, GNUNET_NO); | ||
1819 | mq = GNUNET_malloc (sizeof(struct MessageQueue) + msg_size); | ||
1820 | mq->cont = cont; | ||
1821 | mq->cont_cls = cont_cls; | ||
1822 | GNUNET_memcpy (&mq[1], msg, msg_size); | ||
1823 | mq->message_buf = (const char *) &mq[1]; | ||
1824 | mq->message_buf_size = msg_size; | ||
1825 | mq->timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1826 | |||
1827 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1828 | "Enqueueing %u bytes to send to peer %s\n", | ||
1829 | (unsigned int) msg_size, | ||
1830 | GNUNET_i2s (target)); | ||
1831 | GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, | ||
1832 | n->messages_tail, | ||
1833 | mq); | ||
1834 | if (NULL != n->task) | ||
1835 | GNUNET_SCHEDULER_cancel (n->task); | ||
1836 | n->task = GNUNET_SCHEDULER_add_now (&master_task, n); | ||
1837 | } | ||
1838 | |||
1839 | |||
1840 | /** | ||
1841 | * Continuation called from our attempt to transmitted our | ||
1842 | * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN to the specified @a | ||
1843 | * target. Continue processing based on the @a result. Specifically, | ||
1844 | * if we failed to transmit, discard the address we used. | ||
1845 | * | ||
1846 | * @param cls NULL | ||
1847 | * @param target which peer received the transmission | ||
1848 | * @param result #GNUNET_OK if sending worked | ||
1849 | * @param size_payload how many bytes of payload were sent (ignored) | ||
1850 | * @param size_on_wire how much bandwidth was consumed on the wire (ignored) | ||
1851 | */ | ||
1852 | static void | ||
1853 | send_session_syn_cont (void *cls, | ||
1854 | const struct GNUNET_PeerIdentity *target, | ||
1855 | int result, | ||
1856 | size_t size_payload, | ||
1857 | size_t size_on_wire) | ||
1858 | { | ||
1859 | struct NeighbourMapEntry *n; | ||
1860 | |||
1861 | (void) cls; | ||
1862 | (void) size_payload; | ||
1863 | (void) size_on_wire; | ||
1864 | n = lookup_neighbour (target); | ||
1865 | if (NULL == n) | ||
1866 | { | ||
1867 | /* SYN continuation was called after neighbor was freed, | ||
1868 | * for example due to a time out for the state or the session | ||
1869 | * used was already terminated: nothing to do here... */ | ||
1870 | return; | ||
1871 | } | ||
1872 | |||
1873 | if ((GNUNET_TRANSPORT_PS_SYN_SENT != n->state) && | ||
1874 | (GNUNET_TRANSPORT_PS_RECONNECT_SENT != n->state) && | ||
1875 | (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT != n->state)) | ||
1876 | { | ||
1877 | /* SYN continuation was called after neighbor changed state, | ||
1878 | * for example due to a time out for the state or the session | ||
1879 | * used was already terminated: nothing to do here... */ | ||
1880 | return; | ||
1881 | } | ||
1882 | if (GNUNET_OK == result) | ||
1883 | return; | ||
1884 | |||
1885 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1886 | _ ("Failed to send SYN message to peer `%s'\n"), | ||
1887 | GNUNET_i2s (target)); | ||
1888 | switch (n->state) | ||
1889 | { | ||
1890 | case GNUNET_TRANSPORT_PS_SYN_SENT: | ||
1891 | /* Remove address and request an additional one */ | ||
1892 | unset_primary_address (n); | ||
1893 | set_state_and_timeout (n, | ||
1894 | GNUNET_TRANSPORT_PS_INIT_ATS, | ||
1895 | GNUNET_TIME_relative_to_absolute ( | ||
1896 | FAST_RECONNECT_TIMEOUT)); | ||
1897 | break; | ||
1898 | |||
1899 | case GNUNET_TRANSPORT_PS_RECONNECT_SENT: | ||
1900 | /* Remove address and request an additional one */ | ||
1901 | unset_primary_address (n); | ||
1902 | set_state_and_timeout (n, | ||
1903 | GNUNET_TRANSPORT_PS_RECONNECT_ATS, | ||
1904 | GNUNET_TIME_relative_to_absolute ( | ||
1905 | ATS_RESPONSE_TIMEOUT)); | ||
1906 | break; | ||
1907 | |||
1908 | case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: | ||
1909 | /* Remove address and request and go back to primary address */ | ||
1910 | GNUNET_STATISTICS_update (GST_stats, | ||
1911 | gettext_noop ( | ||
1912 | "# Failed attempts to switch addresses (failed to send SYN CONT)"), | ||
1913 | 1, | ||
1914 | GNUNET_NO); | ||
1915 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1916 | "Switch failed, cleaning up alternative address\n"); | ||
1917 | free_address (&n->alternative_address); | ||
1918 | set_state_and_timeout (n, | ||
1919 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
1920 | GNUNET_TIME_relative_to_absolute ( | ||
1921 | ATS_RESPONSE_TIMEOUT)); | ||
1922 | break; | ||
1923 | |||
1924 | default: | ||
1925 | disconnect_neighbour (n); | ||
1926 | break; | ||
1927 | } | ||
1928 | } | ||
1929 | |||
1930 | |||
1931 | /** | ||
1932 | * Send a SYN message via the given address. | ||
1933 | * | ||
1934 | * @param na address to use | ||
1935 | */ | ||
1936 | static void | ||
1937 | send_syn (struct NeighbourAddress *na) | ||
1938 | { | ||
1939 | struct GNUNET_TRANSPORT_PluginFunctions *papi; | ||
1940 | struct TransportSynMessage connect_msg; | ||
1941 | struct NeighbourMapEntry *n; | ||
1942 | |||
1943 | GNUNET_assert (NULL != na->session); | ||
1944 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1945 | "Sending SYN message to peer `%s' at %s\n", | ||
1946 | GNUNET_i2s (&na->address->peer), | ||
1947 | GST_plugins_a2s (na->address)); | ||
1948 | |||
1949 | papi = GST_plugins_find (na->address->transport_name); | ||
1950 | GNUNET_assert (NULL != papi); | ||
1951 | GNUNET_STATISTICS_update (GST_stats, | ||
1952 | gettext_noop | ||
1953 | ("# SYN messages sent"), | ||
1954 | 1, GNUNET_NO); | ||
1955 | na->connect_timestamp = GNUNET_TIME_absolute_get (); | ||
1956 | connect_msg.header.size = htons (sizeof(struct TransportSynMessage)); | ||
1957 | connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN); | ||
1958 | connect_msg.reserved = htonl (0); | ||
1959 | connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp); | ||
1960 | if (-1 == | ||
1961 | papi->send (papi->cls, | ||
1962 | na->session, | ||
1963 | (const char *) &connect_msg, | ||
1964 | sizeof(struct TransportSynMessage), | ||
1965 | UINT_MAX, | ||
1966 | SETUP_CONNECTION_TIMEOUT, | ||
1967 | &send_session_syn_cont, NULL)) | ||
1968 | { | ||
1969 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1970 | _ ("Failed to transmit SYN message to %s\n"), | ||
1971 | GST_plugins_a2s (na->address)); | ||
1972 | n = lookup_neighbour (&na->address->peer); | ||
1973 | if (NULL == n) | ||
1974 | { | ||
1975 | GNUNET_break (0); | ||
1976 | return; | ||
1977 | } | ||
1978 | switch (n->state) | ||
1979 | { | ||
1980 | case GNUNET_TRANSPORT_PS_SYN_SENT: | ||
1981 | /* Remove address and request and additional one */ | ||
1982 | GNUNET_assert (na == &n->primary_address); | ||
1983 | unset_primary_address (n); | ||
1984 | set_state_and_timeout (n, | ||
1985 | GNUNET_TRANSPORT_PS_INIT_ATS, | ||
1986 | GNUNET_TIME_relative_to_absolute ( | ||
1987 | FAST_RECONNECT_TIMEOUT)); | ||
1988 | /* Hard failure to send the SYN message with this address: | ||
1989 | Destroy address and session */ | ||
1990 | break; | ||
1991 | |||
1992 | case GNUNET_TRANSPORT_PS_RECONNECT_SENT: | ||
1993 | /* Remove address and request an additional one */ | ||
1994 | GNUNET_assert (na == &n->primary_address); | ||
1995 | unset_primary_address (n); | ||
1996 | set_state_and_timeout (n, | ||
1997 | GNUNET_TRANSPORT_PS_RECONNECT_ATS, | ||
1998 | GNUNET_TIME_relative_to_absolute ( | ||
1999 | ATS_RESPONSE_TIMEOUT)); | ||
2000 | break; | ||
2001 | |||
2002 | case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: | ||
2003 | GNUNET_assert (na == &n->alternative_address); | ||
2004 | GNUNET_STATISTICS_update (GST_stats, | ||
2005 | gettext_noop ( | ||
2006 | "# Failed attempts to switch addresses (failed to send SYN)"), | ||
2007 | 1, | ||
2008 | GNUNET_NO); | ||
2009 | /* Remove address and request an additional one */ | ||
2010 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2011 | "Switch failed, cleaning up alternative address\n"); | ||
2012 | free_address (&n->alternative_address); | ||
2013 | set_state_and_timeout (n, | ||
2014 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
2015 | GNUNET_TIME_relative_to_absolute ( | ||
2016 | ATS_RESPONSE_TIMEOUT)); | ||
2017 | break; | ||
2018 | |||
2019 | default: | ||
2020 | GNUNET_break (0); | ||
2021 | disconnect_neighbour (n); | ||
2022 | break; | ||
2023 | } | ||
2024 | return; | ||
2025 | } | ||
2026 | GST_neighbours_notify_data_sent (na->address, | ||
2027 | na->session, | ||
2028 | sizeof(struct TransportSynMessage)); | ||
2029 | } | ||
2030 | |||
2031 | |||
2032 | /** | ||
2033 | * Continuation called from our attempt to transmitted our | ||
2034 | * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK to the specified @a | ||
2035 | * target. Continue processing based on the @a result. Specifically, | ||
2036 | * if we failed to transmit, discard the address we used. | ||
2037 | * | ||
2038 | * @param cls NULL | ||
2039 | * @param target which peer received the transmission | ||
2040 | * @param result #GNUNET_OK if sending worked | ||
2041 | * @param size_payload how many bytes of payload were sent (ignored) | ||
2042 | * @param size_on_wire how much bandwidth was consumed on the wire (ignored) | ||
2043 | */ | ||
2044 | static void | ||
2045 | send_session_syn_ack_cont (void *cls, | ||
2046 | const struct GNUNET_PeerIdentity *target, | ||
2047 | int result, | ||
2048 | size_t size_payload, | ||
2049 | size_t size_on_wire) | ||
2050 | { | ||
2051 | struct NeighbourMapEntry *n; | ||
2052 | |||
2053 | (void) cls; | ||
2054 | (void) size_payload; | ||
2055 | (void) size_on_wire; | ||
2056 | n = lookup_neighbour (target); | ||
2057 | if (NULL == n) | ||
2058 | { | ||
2059 | /* SYN_ACK continuation was called after neighbor was freed, | ||
2060 | * for example due to a time out for the state or the session | ||
2061 | * used was already terminated: nothing to do here... */ | ||
2062 | return; | ||
2063 | } | ||
2064 | |||
2065 | if (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state) | ||
2066 | { | ||
2067 | /* SYN_ACK continuation was called after neighbor changed state, | ||
2068 | * for example due to a time out for the state or the session | ||
2069 | * used was already terminated: nothing to do here... */ | ||
2070 | return; | ||
2071 | } | ||
2072 | if (GNUNET_OK == result) | ||
2073 | return; | ||
2074 | |||
2075 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
2076 | _ ( | ||
2077 | "Failed to send SYN_ACK message to peer `%s' using address `%s'\n"), | ||
2078 | GNUNET_i2s (target), | ||
2079 | GST_plugins_a2s (n->primary_address.address)); | ||
2080 | |||
2081 | /* Remove address and request and additional one */ | ||
2082 | /* FIXME: what if the neighbour's primary address | ||
2083 | changed in the meantime? Might want to instead | ||
2084 | pass "something" around in closure to be sure. */ | ||
2085 | unset_primary_address (n); | ||
2086 | n->ack_state = ACK_SEND_SYN_ACK; | ||
2087 | set_state_and_timeout (n, | ||
2088 | GNUNET_TRANSPORT_PS_SYN_RECV_ATS, | ||
2089 | GNUNET_TIME_relative_to_absolute ( | ||
2090 | ATS_RESPONSE_TIMEOUT)); | ||
2091 | } | ||
2092 | |||
2093 | |||
2094 | /** | ||
2095 | * Send a SYN_ACK message via the given address. | ||
2096 | * | ||
2097 | * @param na address and session to use | ||
2098 | * @param timestamp timestamp to use for the ACK message | ||
2099 | * @return #GNUNET_SYSERR if sending immediately failed, #GNUNET_OK otherwise | ||
2100 | */ | ||
2101 | static void | ||
2102 | send_syn_ack_message (struct NeighbourAddress *na, | ||
2103 | struct GNUNET_TIME_Absolute timestamp) | ||
2104 | { | ||
2105 | const struct GNUNET_HELLO_Address *address = na->address; | ||
2106 | struct GNUNET_ATS_Session *session = na->session; | ||
2107 | struct GNUNET_TRANSPORT_PluginFunctions *papi; | ||
2108 | struct TransportSynMessage connect_msg; | ||
2109 | struct NeighbourMapEntry *n; | ||
2110 | |||
2111 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
2112 | "Sending SYN_ACK to peer `%s'\n", | ||
2113 | GNUNET_i2s (&address->peer)); | ||
2114 | |||
2115 | if (NULL == (papi = GST_plugins_find (address->transport_name))) | ||
2116 | { | ||
2117 | GNUNET_break (0); | ||
2118 | return; | ||
2119 | } | ||
2120 | if (NULL == session) | ||
2121 | session = papi->get_session (papi->cls, | ||
2122 | address); | ||
2123 | if (NULL == session) | ||
2124 | { | ||
2125 | GNUNET_break (0); | ||
2126 | return; | ||
2127 | } | ||
2128 | GST_ats_new_session (address, | ||
2129 | session); | ||
2130 | GNUNET_STATISTICS_update (GST_stats, | ||
2131 | gettext_noop | ||
2132 | ("# SYN_ACK messages sent"), | ||
2133 | 1, GNUNET_NO); | ||
2134 | connect_msg.header.size = htons (sizeof(struct TransportSynMessage)); | ||
2135 | connect_msg.header.type = htons ( | ||
2136 | GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK); | ||
2137 | connect_msg.reserved = htonl (0); | ||
2138 | connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp); | ||
2139 | |||
2140 | if (GNUNET_SYSERR == | ||
2141 | papi->send (papi->cls, | ||
2142 | session, | ||
2143 | (const char *) &connect_msg, | ||
2144 | sizeof(struct TransportSynMessage), | ||
2145 | UINT_MAX, | ||
2146 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
2147 | &send_session_syn_ack_cont, NULL)) | ||
2148 | { | ||
2149 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2150 | _ ("Failed to transmit SYN_ACK message to %s\n"), | ||
2151 | GST_plugins_a2s (address)); | ||
2152 | |||
2153 | n = lookup_neighbour (&address->peer); | ||
2154 | if (NULL == n) | ||
2155 | { | ||
2156 | GNUNET_break (0); | ||
2157 | return; | ||
2158 | } | ||
2159 | /* Remove address and request and additional one */ | ||
2160 | unset_primary_address (n); | ||
2161 | n->ack_state = ACK_SEND_SYN_ACK; | ||
2162 | set_state_and_timeout (n, | ||
2163 | GNUNET_TRANSPORT_PS_SYN_RECV_ATS, | ||
2164 | GNUNET_TIME_relative_to_absolute ( | ||
2165 | ATS_RESPONSE_TIMEOUT)); | ||
2166 | return; | ||
2167 | } | ||
2168 | } | ||
2169 | |||
2170 | |||
2171 | /** | ||
2172 | * Function called by the bandwidth tracker for a peer whenever | ||
2173 | * the tracker's state changed such that we need to recalculate | ||
2174 | * the delay for flow control. We calculate the latest delay | ||
2175 | * and inform the plugin (if applicable). | ||
2176 | * | ||
2177 | * @param cls the `struct NeighbourMapEntry` to update calculations for | ||
2178 | */ | ||
2179 | static void | ||
2180 | inbound_bw_tracker_update (void *cls) | ||
2181 | { | ||
2182 | struct NeighbourMapEntry *n = cls; | ||
2183 | struct GNUNET_TRANSPORT_PluginFunctions *papi; | ||
2184 | struct GNUNET_TIME_Relative delay; | ||
2185 | int do_forward; | ||
2186 | |||
2187 | if (NULL == n->primary_address.address) | ||
2188 | return; /* not active, ignore */ | ||
2189 | papi = GST_plugins_find (n->primary_address.address->transport_name); | ||
2190 | GNUNET_assert (NULL != papi); | ||
2191 | if (NULL == papi->update_inbound_delay) | ||
2192 | return; | ||
2193 | delay = GST_neighbours_calculate_receive_delay (&n->id, | ||
2194 | 0, | ||
2195 | &do_forward); | ||
2196 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2197 | "New inbound delay for peer `%s' is %llu ms\n", | ||
2198 | GNUNET_i2s (&n->id), | ||
2199 | (unsigned long long) delay.rel_value_us / 1000LL); | ||
2200 | if (NULL == n->primary_address.session) | ||
2201 | return; | ||
2202 | papi->update_inbound_delay (papi->cls, | ||
2203 | &n->id, | ||
2204 | n->primary_address.session, | ||
2205 | delay); | ||
2206 | } | ||
2207 | |||
2208 | |||
2209 | /** | ||
2210 | * Create a fresh entry in the neighbour map for the given peer | ||
2211 | * | ||
2212 | * @param peer peer to create an entry for | ||
2213 | * @return new neighbour map entry | ||
2214 | */ | ||
2215 | static struct NeighbourMapEntry * | ||
2216 | setup_neighbour (const struct GNUNET_PeerIdentity *peer) | ||
2217 | { | ||
2218 | struct NeighbourMapEntry *n; | ||
2219 | |||
2220 | if (0 == | ||
2221 | memcmp (&GST_my_identity, | ||
2222 | peer, | ||
2223 | sizeof(struct GNUNET_PeerIdentity))) | ||
2224 | { | ||
2225 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2226 | "Cowardly refusing to consider myself my neighbour!\n"); | ||
2227 | return NULL; | ||
2228 | } | ||
2229 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2230 | "Creating new neighbour entry for `%s'\n", | ||
2231 | GNUNET_i2s (peer)); | ||
2232 | n = GNUNET_new (struct NeighbourMapEntry); | ||
2233 | n->id = *peer; | ||
2234 | n->ack_state = ACK_UNDEFINED; | ||
2235 | n->last_util_transmission = GNUNET_TIME_absolute_get (); | ||
2236 | n->neighbour_receive_quota = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT; | ||
2237 | GNUNET_BANDWIDTH_tracker_init (&n->in_tracker, | ||
2238 | &inbound_bw_tracker_update, | ||
2239 | n, | ||
2240 | GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT, | ||
2241 | MAX_BANDWIDTH_CARRY_S); | ||
2242 | n->task = GNUNET_SCHEDULER_add_now (&master_task, n); | ||
2243 | set_state_and_timeout (n, | ||
2244 | GNUNET_TRANSPORT_PS_NOT_CONNECTED, | ||
2245 | GNUNET_TIME_UNIT_FOREVER_ABS); | ||
2246 | GNUNET_assert (GNUNET_OK == | ||
2247 | GNUNET_CONTAINER_multipeermap_put (neighbours, | ||
2248 | &n->id, | ||
2249 | n, | ||
2250 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
2251 | n->suggest_handle = GNUNET_ATS_connectivity_suggest (GST_ats_connect, | ||
2252 | peer, | ||
2253 | 0); | ||
2254 | |||
2255 | return n; | ||
2256 | } | ||
2257 | |||
2258 | |||
2259 | /** | ||
2260 | * Entry in a DLL we use to keep track of pending blacklist checks. | ||
2261 | */ | ||
2262 | struct BlacklistCheckSwitchContext | ||
2263 | { | ||
2264 | /** | ||
2265 | * DLL prev pointer. | ||
2266 | */ | ||
2267 | struct BlacklistCheckSwitchContext *prev; | ||
2268 | |||
2269 | /** | ||
2270 | * DLL next pointer. | ||
2271 | */ | ||
2272 | struct BlacklistCheckSwitchContext *next; | ||
2273 | |||
2274 | /** | ||
2275 | * Handle to the blacklist check we are performing. | ||
2276 | */ | ||
2277 | struct GST_BlacklistCheck *blc; | ||
2278 | |||
2279 | /** | ||
2280 | * Inbound bandwidth that was assigned to @e address. | ||
2281 | */ | ||
2282 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in; | ||
2283 | |||
2284 | /** | ||
2285 | * Outbound bandwidth that was assigned to @e address. | ||
2286 | */ | ||
2287 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out; | ||
2288 | }; | ||
2289 | |||
2290 | |||
2291 | /** | ||
2292 | * We received a 'SYN' message from the other peer. | ||
2293 | * Consider switching to it. | ||
2294 | * | ||
2295 | * @param message possibly a `struct TransportSynMessage` (check format) | ||
2296 | * @param peer identity of the peer to switch the address for | ||
2297 | * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error | ||
2298 | */ | ||
2299 | int | ||
2300 | GST_neighbours_handle_session_syn (const struct GNUNET_MessageHeader *message, | ||
2301 | const struct GNUNET_PeerIdentity *peer) | ||
2302 | { | ||
2303 | const struct TransportSynMessage *scm; | ||
2304 | struct NeighbourMapEntry *n; | ||
2305 | struct GNUNET_TIME_Absolute ts; | ||
2306 | |||
2307 | if (ntohs (message->size) != sizeof(struct TransportSynMessage)) | ||
2308 | { | ||
2309 | GNUNET_break_op (0); | ||
2310 | return GNUNET_SYSERR; | ||
2311 | } | ||
2312 | GNUNET_STATISTICS_update (GST_stats, | ||
2313 | gettext_noop | ||
2314 | ("# SYN messages received"), | ||
2315 | 1, GNUNET_NO); | ||
2316 | if (NULL == neighbours) | ||
2317 | { | ||
2318 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
2319 | _ ( | ||
2320 | "SYN request from peer `%s' ignored due impending shutdown\n"), | ||
2321 | GNUNET_i2s (peer)); | ||
2322 | return GNUNET_OK; /* we're shutting down */ | ||
2323 | } | ||
2324 | scm = (const struct TransportSynMessage *) message; | ||
2325 | GNUNET_break_op (0 == ntohl (scm->reserved)); | ||
2326 | ts = GNUNET_TIME_absolute_ntoh (scm->timestamp); | ||
2327 | if (0 == | ||
2328 | memcmp (&GST_my_identity, | ||
2329 | peer, | ||
2330 | sizeof(struct GNUNET_PeerIdentity))) | ||
2331 | { | ||
2332 | /* loopback connection-to-self, ignore */ | ||
2333 | return GNUNET_SYSERR; | ||
2334 | } | ||
2335 | n = lookup_neighbour (peer); | ||
2336 | if (NULL == n) | ||
2337 | { | ||
2338 | /* This is a new neighbour and set to not connected */ | ||
2339 | n = setup_neighbour (peer); | ||
2340 | GNUNET_assert (NULL != n); | ||
2341 | } | ||
2342 | |||
2343 | /* Remember this SYN message in neighbour */ | ||
2344 | n->ack_state = ACK_SEND_SYN_ACK; | ||
2345 | n->connect_ack_timestamp = ts; | ||
2346 | |||
2347 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
2348 | "Received SYN for peer `%s' in state %s/%s\n", | ||
2349 | GNUNET_i2s (peer), | ||
2350 | GNUNET_TRANSPORT_ps2s (n->state), | ||
2351 | print_ack_state (n->ack_state)); | ||
2352 | |||
2353 | switch (n->state) | ||
2354 | { | ||
2355 | case GNUNET_TRANSPORT_PS_NOT_CONNECTED: | ||
2356 | /* Request an address from ATS to send SYN_ACK to this peer */ | ||
2357 | set_state_and_timeout (n, | ||
2358 | GNUNET_TRANSPORT_PS_SYN_RECV_ATS, | ||
2359 | GNUNET_TIME_relative_to_absolute ( | ||
2360 | ATS_RESPONSE_TIMEOUT)); | ||
2361 | break; | ||
2362 | |||
2363 | case GNUNET_TRANSPORT_PS_INIT_ATS: | ||
2364 | /* SYN message takes priority over us asking ATS for address: | ||
2365 | * Wait for ATS to suggest an address and send SYN_ACK */ | ||
2366 | set_state_and_timeout (n, | ||
2367 | GNUNET_TRANSPORT_PS_SYN_RECV_ATS, | ||
2368 | GNUNET_TIME_relative_to_absolute ( | ||
2369 | ATS_RESPONSE_TIMEOUT)); | ||
2370 | break; | ||
2371 | |||
2372 | case GNUNET_TRANSPORT_PS_SYN_RECV_ATS: | ||
2373 | /* We already wait for an address to send an SYN_ACK */ | ||
2374 | break; | ||
2375 | |||
2376 | case GNUNET_TRANSPORT_PS_SYN_SENT: | ||
2377 | case GNUNET_TRANSPORT_PS_SYN_RECV_ACK: | ||
2378 | /* Send ACK immediately */ | ||
2379 | n->ack_state = ACK_SEND_ACK; | ||
2380 | send_syn_ack_message (&n->primary_address, | ||
2381 | ts); | ||
2382 | break; | ||
2383 | |||
2384 | case GNUNET_TRANSPORT_PS_CONNECTED: | ||
2385 | /* we are already connected and can thus send the ACK immediately */ | ||
2386 | GNUNET_assert (NULL != n->primary_address.address); | ||
2387 | GNUNET_assert (NULL != n->primary_address.session); | ||
2388 | n->ack_state = ACK_SEND_ACK; | ||
2389 | send_syn_ack_message (&n->primary_address, | ||
2390 | ts); | ||
2391 | break; | ||
2392 | |||
2393 | case GNUNET_TRANSPORT_PS_RECONNECT_ATS: | ||
2394 | /* We wait for ATS address suggestion */ | ||
2395 | break; | ||
2396 | |||
2397 | case GNUNET_TRANSPORT_PS_RECONNECT_SENT: | ||
2398 | /* We received a SYN message while waiting for a SYN_ACK in fast | ||
2399 | * reconnect. Send SYN_ACK immediately */ | ||
2400 | n->ack_state = ACK_SEND_ACK; | ||
2401 | send_syn_ack_message (&n->primary_address, | ||
2402 | n->connect_ack_timestamp); | ||
2403 | break; | ||
2404 | |||
2405 | case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: | ||
2406 | /* We are already connected and can thus send the ACK immediately; | ||
2407 | still, it can never hurt to have an alternative address, so also | ||
2408 | tell ATS about it */ | ||
2409 | GNUNET_assert (NULL != n->primary_address.address); | ||
2410 | GNUNET_assert (NULL != n->primary_address.session); | ||
2411 | n->ack_state = ACK_SEND_ACK; | ||
2412 | send_syn_ack_message (&n->primary_address, | ||
2413 | ts); | ||
2414 | break; | ||
2415 | |||
2416 | case GNUNET_TRANSPORT_PS_DISCONNECT: | ||
2417 | /* Get rid of remains and re-try */ | ||
2418 | free_neighbour (n); | ||
2419 | n = setup_neighbour (peer); | ||
2420 | GNUNET_assert (NULL != n); | ||
2421 | /* Remember the SYN time stamp for ACK message */ | ||
2422 | n->ack_state = ACK_SEND_SYN_ACK; | ||
2423 | n->connect_ack_timestamp = ts; | ||
2424 | /* Request an address for the peer */ | ||
2425 | set_state_and_timeout (n, | ||
2426 | GNUNET_TRANSPORT_PS_SYN_RECV_ATS, | ||
2427 | GNUNET_TIME_relative_to_absolute ( | ||
2428 | ATS_RESPONSE_TIMEOUT)); | ||
2429 | break; | ||
2430 | |||
2431 | case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED: | ||
2432 | /* should not be possible */ | ||
2433 | GNUNET_assert (0); | ||
2434 | break; | ||
2435 | |||
2436 | default: | ||
2437 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2438 | "Unhandled state `%s'\n", | ||
2439 | GNUNET_TRANSPORT_ps2s (n->state)); | ||
2440 | GNUNET_break (0); | ||
2441 | return GNUNET_SYSERR; | ||
2442 | } | ||
2443 | return GNUNET_OK; | ||
2444 | } | ||
2445 | |||
2446 | |||
2447 | /** | ||
2448 | * Check if the given @a address is the same that we are already | ||
2449 | * using for the respective neighbour. If so, update the bandwidth | ||
2450 | * assignment and possibly the session and return #GNUNET_OK. | ||
2451 | * If the new address is different from what the neighbour is | ||
2452 | * using right now, return #GNUNET_NO. | ||
2453 | * | ||
2454 | * @param address address of the other peer, | ||
2455 | * @param session session to use or NULL if transport should initiate a session | ||
2456 | * @param bandwidth_in inbound quota to be used when connection is up, | ||
2457 | * 0 to disconnect from peer | ||
2458 | * @param bandwidth_out outbound quota to be used when connection is up, | ||
2459 | * 0 to disconnect from peer | ||
2460 | * @return #GNUNET_OK if we were able to just update the bandwidth and session, | ||
2461 | * #GNUNET_NO if more extensive changes are required (address changed) | ||
2462 | */ | ||
2463 | static int | ||
2464 | try_run_fast_ats_update (const struct GNUNET_HELLO_Address *address, | ||
2465 | struct GNUNET_ATS_Session *session, | ||
2466 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, | ||
2467 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out) | ||
2468 | { | ||
2469 | struct NeighbourMapEntry *n; | ||
2470 | |||
2471 | n = lookup_neighbour (&address->peer); | ||
2472 | if ((NULL == n) || | ||
2473 | (NULL == n->primary_address.address) || | ||
2474 | (0 != GNUNET_HELLO_address_cmp (address, | ||
2475 | n->primary_address.address))) | ||
2476 | return GNUNET_NO; | ||
2477 | /* We are not really switching addresses, but merely adjusting | ||
2478 | session and/or bandwidth, can do fast ATS update! */ | ||
2479 | if (session != n->primary_address.session) | ||
2480 | { | ||
2481 | /* switch to a different session, but keeping same address; could | ||
2482 | happen if there is a 2nd inbound connection */ | ||
2483 | n->primary_address.session = session; | ||
2484 | GNUNET_assert (GNUNET_YES == | ||
2485 | GST_ats_is_known (n->primary_address.address, | ||
2486 | n->primary_address.session)); | ||
2487 | } | ||
2488 | if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__) | ||
2489 | { | ||
2490 | n->primary_address.bandwidth_in = bandwidth_in; | ||
2491 | if (GNUNET_YES != | ||
2492 | set_incoming_quota (n, | ||
2493 | bandwidth_in)) | ||
2494 | return GNUNET_NO; | ||
2495 | } | ||
2496 | if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__) | ||
2497 | { | ||
2498 | n->primary_address.bandwidth_out = bandwidth_out; | ||
2499 | send_outbound_quota_to_clients (n); | ||
2500 | } | ||
2501 | return GNUNET_OK; | ||
2502 | } | ||
2503 | |||
2504 | |||
2505 | /** | ||
2506 | * We've been asked to switch addresses, and just now got the result | ||
2507 | * from the blacklist check to see if this is allowed. | ||
2508 | * | ||
2509 | * @param cls the `struct BlacklistCheckSwitchContext` with | ||
2510 | * the information about the future address | ||
2511 | * @param peer the peer we may switch addresses on | ||
2512 | * @param address address associated with the request | ||
2513 | * @param session session associated with the request | ||
2514 | * @param result #GNUNET_OK if the connection is allowed, | ||
2515 | * #GNUNET_NO if not, | ||
2516 | * #GNUNET_SYSERR if operation was aborted | ||
2517 | */ | ||
2518 | static void | ||
2519 | switch_address_bl_check_cont (void *cls, | ||
2520 | const struct GNUNET_PeerIdentity *peer, | ||
2521 | const struct GNUNET_HELLO_Address *address, | ||
2522 | struct GNUNET_ATS_Session *session, | ||
2523 | int result) | ||
2524 | { | ||
2525 | struct BlacklistCheckSwitchContext *blc_ctx = cls; | ||
2526 | struct GNUNET_TRANSPORT_PluginFunctions *papi; | ||
2527 | struct NeighbourMapEntry *n; | ||
2528 | |||
2529 | if (GNUNET_SYSERR == result) | ||
2530 | goto cleanup; | ||
2531 | |||
2532 | papi = GST_plugins_find (address->transport_name); | ||
2533 | if (NULL == papi) | ||
2534 | { | ||
2535 | /* This can happen during shutdown. */ | ||
2536 | goto cleanup; | ||
2537 | } | ||
2538 | |||
2539 | if (GNUNET_NO == result) | ||
2540 | { | ||
2541 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2542 | "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n", | ||
2543 | GST_plugins_a2s (address), | ||
2544 | session, | ||
2545 | GNUNET_i2s (peer)); | ||
2546 | GNUNET_STATISTICS_update (GST_stats, | ||
2547 | "# ATS suggestions ignored (blacklist denied)", | ||
2548 | 1, | ||
2549 | GNUNET_NO); | ||
2550 | if (NULL != session) | ||
2551 | papi->disconnect_session (papi->cls, | ||
2552 | session); | ||
2553 | if (GNUNET_YES != | ||
2554 | GNUNET_HELLO_address_check_option (address, | ||
2555 | GNUNET_HELLO_ADDRESS_INFO_INBOUND)) | ||
2556 | GST_ats_block_address (address, | ||
2557 | NULL); | ||
2558 | goto cleanup; | ||
2559 | } | ||
2560 | |||
2561 | |||
2562 | if (NULL == session) | ||
2563 | { | ||
2564 | /* need to create a session, ATS only gave us an address */ | ||
2565 | session = papi->get_session (papi->cls, | ||
2566 | address); | ||
2567 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2568 | "Obtained new session for peer `%s' and address '%s': %p\n", | ||
2569 | GNUNET_i2s (&address->peer), | ||
2570 | GST_plugins_a2s (address), | ||
2571 | session); | ||
2572 | if (NULL != session) | ||
2573 | GST_ats_new_session (address, | ||
2574 | session); | ||
2575 | } | ||
2576 | if (NULL == session) | ||
2577 | { | ||
2578 | /* session creation failed, bad!, fail! */ | ||
2579 | GNUNET_STATISTICS_update (GST_stats, | ||
2580 | "# ATS suggestions ignored (failed to create session)", | ||
2581 | 1, | ||
2582 | GNUNET_NO); | ||
2583 | /* No session could be obtained, remove blacklist check and clean up */ | ||
2584 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2585 | "Failed to obtain new session for peer `%s' and address '%s'\n", | ||
2586 | GNUNET_i2s (&address->peer), | ||
2587 | GST_plugins_a2s (address)); | ||
2588 | GST_ats_block_address (address, | ||
2589 | session); | ||
2590 | goto cleanup; | ||
2591 | } | ||
2592 | |||
2593 | /* We did this check already before going into blacklist, but | ||
2594 | it is theoretically possible that the situation changed in | ||
2595 | the meantime, hence we check again here */ | ||
2596 | if (GNUNET_OK == | ||
2597 | try_run_fast_ats_update (address, | ||
2598 | session, | ||
2599 | blc_ctx->bandwidth_in, | ||
2600 | blc_ctx->bandwidth_out)) | ||
2601 | goto cleanup; /* was just a minor update, we're done */ | ||
2602 | |||
2603 | /* check if we also need to setup the neighbour entry */ | ||
2604 | if (NULL == (n = lookup_neighbour (peer))) | ||
2605 | { | ||
2606 | n = setup_neighbour (peer); | ||
2607 | if (NULL == n) | ||
2608 | { | ||
2609 | /* not sure how this can happen... */ | ||
2610 | GNUNET_break (0); | ||
2611 | goto cleanup; | ||
2612 | } | ||
2613 | n->state = GNUNET_TRANSPORT_PS_INIT_ATS; | ||
2614 | } | ||
2615 | |||
2616 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
2617 | "Peer `%s' switches to address `%s'\n", | ||
2618 | GNUNET_i2s (&address->peer), | ||
2619 | GST_plugins_a2s (address)); | ||
2620 | |||
2621 | switch (n->state) | ||
2622 | { | ||
2623 | case GNUNET_TRANSPORT_PS_NOT_CONNECTED: | ||
2624 | GNUNET_break (0); | ||
2625 | GST_ats_block_address (address, | ||
2626 | session); | ||
2627 | free_neighbour (n); | ||
2628 | return; | ||
2629 | |||
2630 | case GNUNET_TRANSPORT_PS_INIT_ATS: | ||
2631 | /* We requested an address and ATS suggests one: | ||
2632 | * set primary address and send SYN message*/ | ||
2633 | set_primary_address (n, | ||
2634 | address, | ||
2635 | session, | ||
2636 | blc_ctx->bandwidth_in, | ||
2637 | blc_ctx->bandwidth_out); | ||
2638 | if (ACK_SEND_SYN_ACK == n->ack_state) | ||
2639 | { | ||
2640 | /* Send pending SYN_ACK message */ | ||
2641 | n->ack_state = ACK_SEND_ACK; | ||
2642 | send_syn_ack_message (&n->primary_address, | ||
2643 | n->connect_ack_timestamp); | ||
2644 | } | ||
2645 | set_state_and_timeout (n, | ||
2646 | GNUNET_TRANSPORT_PS_SYN_SENT, | ||
2647 | GNUNET_TIME_relative_to_absolute ( | ||
2648 | SETUP_CONNECTION_TIMEOUT)); | ||
2649 | send_syn (&n->primary_address); | ||
2650 | break; | ||
2651 | |||
2652 | case GNUNET_TRANSPORT_PS_SYN_SENT: | ||
2653 | /* ATS suggested a new address while waiting for an SYN_ACK: | ||
2654 | * Switch and send new SYN */ | ||
2655 | /* ATS suggests a different address, switch again */ | ||
2656 | set_primary_address (n, | ||
2657 | address, | ||
2658 | session, | ||
2659 | blc_ctx->bandwidth_in, | ||
2660 | blc_ctx->bandwidth_out); | ||
2661 | if (ACK_SEND_SYN_ACK == n->ack_state) | ||
2662 | { | ||
2663 | /* Send pending SYN_ACK message */ | ||
2664 | n->ack_state = ACK_SEND_ACK; | ||
2665 | send_syn_ack_message (&n->primary_address, | ||
2666 | n->connect_ack_timestamp); | ||
2667 | } | ||
2668 | set_state_and_timeout (n, | ||
2669 | GNUNET_TRANSPORT_PS_SYN_SENT, | ||
2670 | GNUNET_TIME_relative_to_absolute ( | ||
2671 | SETUP_CONNECTION_TIMEOUT)); | ||
2672 | send_syn (&n->primary_address); | ||
2673 | break; | ||
2674 | |||
2675 | case GNUNET_TRANSPORT_PS_SYN_RECV_ATS: | ||
2676 | /* We requested an address and ATS suggests one: | ||
2677 | * set primary address and send SYN_ACK message*/ | ||
2678 | set_primary_address (n, | ||
2679 | address, | ||
2680 | session, | ||
2681 | blc_ctx->bandwidth_in, | ||
2682 | blc_ctx->bandwidth_out); | ||
2683 | /* Send an ACK message as a response to the SYN msg */ | ||
2684 | set_state_and_timeout (n, | ||
2685 | GNUNET_TRANSPORT_PS_SYN_RECV_ACK, | ||
2686 | GNUNET_TIME_relative_to_absolute ( | ||
2687 | SETUP_CONNECTION_TIMEOUT)); | ||
2688 | send_syn_ack_message (&n->primary_address, | ||
2689 | n->connect_ack_timestamp); | ||
2690 | if ((ACK_SEND_SYN_ACK == n->ack_state) || | ||
2691 | (ACK_UNDEFINED == n->ack_state)) | ||
2692 | n->ack_state = ACK_SEND_ACK; | ||
2693 | break; | ||
2694 | |||
2695 | case GNUNET_TRANSPORT_PS_SYN_RECV_ACK: | ||
2696 | /* ATS asks us to switch while we were trying to connect; switch to new | ||
2697 | address and check blacklist again */ | ||
2698 | if ((ACK_SEND_SYN_ACK == n->ack_state)) | ||
2699 | { | ||
2700 | n->ack_state = ACK_SEND_ACK; | ||
2701 | send_syn_ack_message (&n->primary_address, | ||
2702 | n->connect_ack_timestamp); | ||
2703 | } | ||
2704 | set_primary_address (n, | ||
2705 | address, | ||
2706 | session, | ||
2707 | blc_ctx->bandwidth_in, | ||
2708 | blc_ctx->bandwidth_out); | ||
2709 | set_state_and_timeout (n, | ||
2710 | GNUNET_TRANSPORT_PS_SYN_RECV_ACK, | ||
2711 | GNUNET_TIME_relative_to_absolute ( | ||
2712 | SETUP_CONNECTION_TIMEOUT)); | ||
2713 | break; | ||
2714 | |||
2715 | case GNUNET_TRANSPORT_PS_CONNECTED: | ||
2716 | GNUNET_assert (NULL != n->primary_address.address); | ||
2717 | GNUNET_assert (NULL != n->primary_address.session); | ||
2718 | GNUNET_break (n->primary_address.session != session); | ||
2719 | /* ATS asks us to switch a life connection; see if we can get | ||
2720 | a SYN_ACK on it before we actually do this! */ | ||
2721 | set_alternative_address (n, | ||
2722 | address, | ||
2723 | session, | ||
2724 | blc_ctx->bandwidth_in, | ||
2725 | blc_ctx->bandwidth_out); | ||
2726 | set_state_and_timeout (n, | ||
2727 | GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT, | ||
2728 | GNUNET_TIME_relative_to_absolute ( | ||
2729 | SETUP_CONNECTION_TIMEOUT)); | ||
2730 | GNUNET_STATISTICS_update (GST_stats, | ||
2731 | gettext_noop ("# Attempts to switch addresses"), | ||
2732 | 1, | ||
2733 | GNUNET_NO); | ||
2734 | send_syn (&n->alternative_address); | ||
2735 | break; | ||
2736 | |||
2737 | case GNUNET_TRANSPORT_PS_RECONNECT_ATS: | ||
2738 | set_primary_address (n, | ||
2739 | address, | ||
2740 | session, | ||
2741 | blc_ctx->bandwidth_in, | ||
2742 | blc_ctx->bandwidth_out); | ||
2743 | if (ACK_SEND_SYN_ACK == n->ack_state) | ||
2744 | { | ||
2745 | /* Send pending SYN_ACK message */ | ||
2746 | n->ack_state = ACK_SEND_ACK; | ||
2747 | send_syn_ack_message (&n->primary_address, | ||
2748 | n->connect_ack_timestamp); | ||
2749 | } | ||
2750 | set_state_and_timeout (n, | ||
2751 | GNUNET_TRANSPORT_PS_RECONNECT_SENT, | ||
2752 | GNUNET_TIME_relative_to_absolute ( | ||
2753 | FAST_RECONNECT_TIMEOUT)); | ||
2754 | send_syn (&n->primary_address); | ||
2755 | break; | ||
2756 | |||
2757 | case GNUNET_TRANSPORT_PS_RECONNECT_SENT: | ||
2758 | /* ATS asks us to switch while we were trying to reconnect; switch to new | ||
2759 | address and send SYN again */ | ||
2760 | set_primary_address (n, | ||
2761 | address, | ||
2762 | session, | ||
2763 | blc_ctx->bandwidth_in, | ||
2764 | blc_ctx->bandwidth_out); | ||
2765 | set_state_and_timeout (n, | ||
2766 | GNUNET_TRANSPORT_PS_RECONNECT_SENT, | ||
2767 | GNUNET_TIME_relative_to_absolute ( | ||
2768 | FAST_RECONNECT_TIMEOUT)); | ||
2769 | send_syn (&n->primary_address); | ||
2770 | break; | ||
2771 | |||
2772 | case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: | ||
2773 | if ((0 == GNUNET_HELLO_address_cmp (n->primary_address.address, | ||
2774 | address)) && | ||
2775 | (n->primary_address.session == session)) | ||
2776 | { | ||
2777 | /* ATS switches back to still-active session */ | ||
2778 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2779 | "ATS double-switched, cleaning up alternative address\n"); | ||
2780 | free_address (&n->alternative_address); | ||
2781 | set_state_and_timeout (n, | ||
2782 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
2783 | n->timeout); | ||
2784 | break; | ||
2785 | } | ||
2786 | /* ATS asks us to switch a life connection, send */ | ||
2787 | set_alternative_address (n, | ||
2788 | address, | ||
2789 | session, | ||
2790 | blc_ctx->bandwidth_in, | ||
2791 | blc_ctx->bandwidth_out); | ||
2792 | set_state_and_timeout (n, | ||
2793 | GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT, | ||
2794 | GNUNET_TIME_relative_to_absolute ( | ||
2795 | SETUP_CONNECTION_TIMEOUT)); | ||
2796 | send_syn (&n->alternative_address); | ||
2797 | break; | ||
2798 | |||
2799 | case GNUNET_TRANSPORT_PS_DISCONNECT: | ||
2800 | /* not going to switch addresses while disconnecting */ | ||
2801 | GNUNET_STATISTICS_update (GST_stats, | ||
2802 | "# ATS suggestion ignored (disconnecting)", | ||
2803 | 1, | ||
2804 | GNUNET_NO); | ||
2805 | return; | ||
2806 | |||
2807 | case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED: | ||
2808 | GNUNET_assert (0); | ||
2809 | break; | ||
2810 | |||
2811 | default: | ||
2812 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2813 | "Unhandled state `%s'\n", | ||
2814 | GNUNET_TRANSPORT_ps2s (n->state)); | ||
2815 | GNUNET_break (0); | ||
2816 | break; | ||
2817 | } | ||
2818 | cleanup: | ||
2819 | GNUNET_CONTAINER_DLL_remove (pending_bc_head, | ||
2820 | pending_bc_tail, | ||
2821 | blc_ctx); | ||
2822 | GNUNET_free (blc_ctx); | ||
2823 | } | ||
2824 | |||
2825 | |||
2826 | /** | ||
2827 | * For the given peer, switch to this address. | ||
2828 | * | ||
2829 | * Before accepting this addresses and actively using it, a blacklist check | ||
2830 | * is performed. | ||
2831 | * | ||
2832 | * If any check fails or the suggestion can somehow not be followed, we | ||
2833 | * MUST call #GST_ats_block_address() to tell ATS that the suggestion | ||
2834 | * could not be satisfied and force ATS to do something else. | ||
2835 | * | ||
2836 | * @param address address of the other peer, | ||
2837 | * @param session session to use or NULL if transport should initiate a session | ||
2838 | * @param bandwidth_in inbound quota to be used when connection is up, | ||
2839 | * 0 to disconnect from peer | ||
2840 | * @param bandwidth_out outbound quota to be used when connection is up, | ||
2841 | * 0 to disconnect from peer | ||
2842 | */ | ||
2843 | void | ||
2844 | GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address, | ||
2845 | struct GNUNET_ATS_Session *session, | ||
2846 | struct GNUNET_BANDWIDTH_Value32NBO | ||
2847 | bandwidth_in, | ||
2848 | struct GNUNET_BANDWIDTH_Value32NBO | ||
2849 | bandwidth_out) | ||
2850 | { | ||
2851 | struct GST_BlacklistCheck *blc; | ||
2852 | struct BlacklistCheckSwitchContext *blc_ctx; | ||
2853 | |||
2854 | GNUNET_assert (NULL != address->transport_name); | ||
2855 | if (GNUNET_OK == | ||
2856 | try_run_fast_ats_update (address, | ||
2857 | session, | ||
2858 | bandwidth_in, | ||
2859 | bandwidth_out)) | ||
2860 | return; | ||
2861 | |||
2862 | /* Check if plugin is available */ | ||
2863 | if (NULL == (GST_plugins_find (address->transport_name))) | ||
2864 | { | ||
2865 | /* we don't have the plugin for this address */ | ||
2866 | GNUNET_break (0); | ||
2867 | GST_ats_block_address (address, | ||
2868 | session); | ||
2869 | return; | ||
2870 | } | ||
2871 | if ((NULL == session) && | ||
2872 | (GNUNET_HELLO_address_check_option (address, | ||
2873 | GNUNET_HELLO_ADDRESS_INFO_INBOUND))) | ||
2874 | { | ||
2875 | /* This is a inbound address and we do not have a session to use! */ | ||
2876 | GNUNET_break (0); | ||
2877 | GST_ats_block_address (address, | ||
2878 | session); | ||
2879 | return; | ||
2880 | } | ||
2881 | |||
2882 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2883 | "ATS suggests address '%s' for peer `%s' at %u/%u speed\n", | ||
2884 | GST_plugins_a2s (address), | ||
2885 | GNUNET_i2s (&address->peer), | ||
2886 | (unsigned int) ntohl (bandwidth_in.value__), | ||
2887 | (unsigned int) ntohl (bandwidth_out.value__)); | ||
2888 | |||
2889 | /* Perform blacklist check */ | ||
2890 | blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext); | ||
2891 | blc_ctx->bandwidth_in = bandwidth_in; | ||
2892 | blc_ctx->bandwidth_out = bandwidth_out; | ||
2893 | GNUNET_CONTAINER_DLL_insert (pending_bc_head, | ||
2894 | pending_bc_tail, | ||
2895 | blc_ctx); | ||
2896 | if (NULL != (blc = GST_blacklist_test_allowed (&address->peer, | ||
2897 | address->transport_name, | ||
2898 | &switch_address_bl_check_cont, | ||
2899 | blc_ctx, | ||
2900 | address, | ||
2901 | session))) | ||
2902 | { | ||
2903 | blc_ctx->blc = blc; | ||
2904 | } | ||
2905 | } | ||
2906 | |||
2907 | |||
2908 | /** | ||
2909 | * Function called to send network utilization data to ATS for | ||
2910 | * each active connection. | ||
2911 | * | ||
2912 | * @param cls NULL | ||
2913 | * @param key peer we send utilization data for | ||
2914 | * @param value the `struct NeighbourMapEntry *` with data to send | ||
2915 | * @return #GNUNET_OK (continue to iterate) | ||
2916 | */ | ||
2917 | static int | ||
2918 | send_utilization_data (void *cls, | ||
2919 | const struct GNUNET_PeerIdentity *key, | ||
2920 | void *value) | ||
2921 | { | ||
2922 | struct NeighbourMapEntry *n = value; | ||
2923 | uint32_t bps_in; | ||
2924 | uint32_t bps_out; | ||
2925 | struct GNUNET_TIME_Relative delta; | ||
2926 | |||
2927 | (void) cls; | ||
2928 | if ((GNUNET_YES != test_connected (n)) || | ||
2929 | (NULL == n->primary_address.address)) | ||
2930 | return GNUNET_OK; | ||
2931 | delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission, | ||
2932 | GNUNET_TIME_absolute_get ()); | ||
2933 | bps_in = 0; | ||
2934 | if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us)) | ||
2935 | bps_in = (1000LL * 1000LL * n->util_total_bytes_recv) | ||
2936 | / (delta.rel_value_us); | ||
2937 | bps_out = 0; | ||
2938 | if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us)) | ||
2939 | bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us; | ||
2940 | |||
2941 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2942 | "`%s' total: received %u Bytes/s, sent %u Bytes/s\n", | ||
2943 | GNUNET_i2s (key), | ||
2944 | bps_in, | ||
2945 | bps_out); | ||
2946 | GST_ats_update_utilization (n->primary_address.address, | ||
2947 | bps_in, | ||
2948 | bps_out); | ||
2949 | n->util_total_bytes_recv = 0; | ||
2950 | n->util_total_bytes_sent = 0; | ||
2951 | n->last_util_transmission = GNUNET_TIME_absolute_get (); | ||
2952 | return GNUNET_OK; | ||
2953 | } | ||
2954 | |||
2955 | |||
2956 | /** | ||
2957 | * Task transmitting utilization in a regular interval | ||
2958 | * | ||
2959 | * @param cls the `struct NeighbourMapEntry` for which we are running | ||
2960 | */ | ||
2961 | static void | ||
2962 | utilization_transmission (void *cls) | ||
2963 | { | ||
2964 | (void) cls; | ||
2965 | util_transmission_tk = NULL; | ||
2966 | GNUNET_CONTAINER_multipeermap_iterate (neighbours, | ||
2967 | &send_utilization_data, | ||
2968 | NULL); | ||
2969 | util_transmission_tk | ||
2970 | = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL, | ||
2971 | &utilization_transmission, | ||
2972 | NULL); | ||
2973 | } | ||
2974 | |||
2975 | |||
2976 | /** | ||
2977 | * Track information about data we received from the | ||
2978 | * given address (used to notify ATS about our utilization | ||
2979 | * of allocated resources). | ||
2980 | * | ||
2981 | * @param address the address we got data from | ||
2982 | * @param message the message we received (really only the size is used) | ||
2983 | */ | ||
2984 | void | ||
2985 | GST_neighbours_notify_data_recv (const struct GNUNET_HELLO_Address *address, | ||
2986 | const struct GNUNET_MessageHeader *message) | ||
2987 | { | ||
2988 | struct NeighbourMapEntry *n; | ||
2989 | |||
2990 | n = lookup_neighbour (&address->peer); | ||
2991 | if (NULL == n) | ||
2992 | return; | ||
2993 | n->util_total_bytes_recv += ntohs (message->size); | ||
2994 | } | ||
2995 | |||
2996 | |||
2997 | /** | ||
2998 | * Track information about data we transmitted using the given @a | ||
2999 | * address and @a session (used to notify ATS about our utilization of | ||
3000 | * allocated resources). | ||
3001 | * | ||
3002 | * @param address the address we transmitted data to | ||
3003 | * @param session session we used to transmit data | ||
3004 | * @param message the message we sent (really only the size is used) | ||
3005 | */ | ||
3006 | void | ||
3007 | GST_neighbours_notify_data_sent (const struct GNUNET_HELLO_Address *address, | ||
3008 | struct GNUNET_ATS_Session *session, | ||
3009 | size_t size) | ||
3010 | { | ||
3011 | struct NeighbourMapEntry *n; | ||
3012 | |||
3013 | n = lookup_neighbour (&address->peer); | ||
3014 | if (NULL == n) | ||
3015 | return; | ||
3016 | if (n->primary_address.session != session) | ||
3017 | return; | ||
3018 | n->util_total_bytes_sent += size; | ||
3019 | } | ||
3020 | |||
3021 | |||
3022 | /** | ||
3023 | * Master task run for every neighbour. Performs all of the time-related | ||
3024 | * activities (keep alive, send next message, disconnect if idle, finish | ||
3025 | * clean up after disconnect). | ||
3026 | * | ||
3027 | * @param cls the 'struct NeighbourMapEntry' for which we are running | ||
3028 | */ | ||
3029 | static void | ||
3030 | master_task (void *cls) | ||
3031 | { | ||
3032 | struct NeighbourMapEntry *n = cls; | ||
3033 | struct GNUNET_TIME_Relative delay; | ||
3034 | |||
3035 | n->task = NULL; | ||
3036 | delay = GNUNET_TIME_absolute_get_remaining (n->timeout); | ||
3037 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3038 | "Master task runs for neighbour `%s' in state %s with timeout in %s\n", | ||
3039 | GNUNET_i2s (&n->id), | ||
3040 | GNUNET_TRANSPORT_ps2s (n->state), | ||
3041 | GNUNET_STRINGS_relative_time_to_string (delay, | ||
3042 | GNUNET_YES)); | ||
3043 | switch (n->state) | ||
3044 | { | ||
3045 | case GNUNET_TRANSPORT_PS_NOT_CONNECTED: | ||
3046 | /* invalid state for master task, clean up */ | ||
3047 | GNUNET_break (0); | ||
3048 | free_neighbour (n); | ||
3049 | return; | ||
3050 | |||
3051 | case GNUNET_TRANSPORT_PS_INIT_ATS: | ||
3052 | if (0 == delay.rel_value_us) | ||
3053 | { | ||
3054 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3055 | "Connection to `%s' timed out waiting for ATS to provide address\n", | ||
3056 | GNUNET_i2s (&n->id)); | ||
3057 | free_neighbour (n); | ||
3058 | return; | ||
3059 | } | ||
3060 | break; | ||
3061 | |||
3062 | case GNUNET_TRANSPORT_PS_SYN_SENT: | ||
3063 | if (0 == delay.rel_value_us) | ||
3064 | { | ||
3065 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3066 | "Connection to `%s' timed out waiting for other peer to send SYN_ACK\n", | ||
3067 | GNUNET_i2s (&n->id)); | ||
3068 | /* Remove address and request and additional one */ | ||
3069 | unset_primary_address (n); | ||
3070 | set_state_and_timeout (n, | ||
3071 | GNUNET_TRANSPORT_PS_INIT_ATS, | ||
3072 | GNUNET_TIME_relative_to_absolute ( | ||
3073 | ATS_RESPONSE_TIMEOUT)); | ||
3074 | return; | ||
3075 | } | ||
3076 | break; | ||
3077 | |||
3078 | case GNUNET_TRANSPORT_PS_SYN_RECV_ATS: | ||
3079 | if (0 == delay.rel_value_us) | ||
3080 | { | ||
3081 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3082 | "Connection to `%s' timed out waiting ATS to provide address to use for SYN_ACK\n", | ||
3083 | GNUNET_i2s (&n->id)); | ||
3084 | free_neighbour (n); | ||
3085 | return; | ||
3086 | } | ||
3087 | break; | ||
3088 | |||
3089 | case GNUNET_TRANSPORT_PS_SYN_RECV_ACK: | ||
3090 | if (0 == delay.rel_value_us) | ||
3091 | { | ||
3092 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3093 | "Connection to `%s' timed out waiting for other peer to send ACK\n", | ||
3094 | GNUNET_i2s (&n->id)); | ||
3095 | disconnect_neighbour (n); | ||
3096 | return; | ||
3097 | } | ||
3098 | break; | ||
3099 | |||
3100 | case GNUNET_TRANSPORT_PS_CONNECTED: | ||
3101 | if (0 == delay.rel_value_us) | ||
3102 | { | ||
3103 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3104 | "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n", | ||
3105 | GNUNET_i2s (&n->id)); | ||
3106 | disconnect_neighbour (n); | ||
3107 | return; | ||
3108 | } | ||
3109 | try_transmission_to_peer (n); | ||
3110 | send_keepalive (n); | ||
3111 | break; | ||
3112 | |||
3113 | case GNUNET_TRANSPORT_PS_RECONNECT_ATS: | ||
3114 | if (0 == delay.rel_value_us) | ||
3115 | { | ||
3116 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3117 | "Connection to `%s' timed out, waiting for ATS replacement address\n", | ||
3118 | GNUNET_i2s (&n->id)); | ||
3119 | disconnect_neighbour (n); | ||
3120 | return; | ||
3121 | } | ||
3122 | break; | ||
3123 | |||
3124 | case GNUNET_TRANSPORT_PS_RECONNECT_SENT: | ||
3125 | if (0 == delay.rel_value_us) | ||
3126 | { | ||
3127 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3128 | "Connection to `%s' timed out, waiting for other peer to SYN_ACK replacement address\n", | ||
3129 | GNUNET_i2s (&n->id)); | ||
3130 | disconnect_neighbour (n); | ||
3131 | return; | ||
3132 | } | ||
3133 | break; | ||
3134 | |||
3135 | case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: | ||
3136 | if (0 == delay.rel_value_us) | ||
3137 | { | ||
3138 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3139 | "Switch failed, cleaning up alternative address\n"); | ||
3140 | free_address (&n->alternative_address); | ||
3141 | set_state_and_timeout (n, | ||
3142 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
3143 | GNUNET_TIME_relative_to_absolute ( | ||
3144 | SETUP_CONNECTION_TIMEOUT)); | ||
3145 | } | ||
3146 | try_transmission_to_peer (n); | ||
3147 | send_keepalive (n); | ||
3148 | break; | ||
3149 | |||
3150 | case GNUNET_TRANSPORT_PS_DISCONNECT: | ||
3151 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3152 | "Cleaning up connection to `%s' after sending DISCONNECT\n", | ||
3153 | GNUNET_i2s (&n->id)); | ||
3154 | free_neighbour (n); | ||
3155 | return; | ||
3156 | |||
3157 | case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED: | ||
3158 | /* how did we get here!? */ | ||
3159 | GNUNET_assert (0); | ||
3160 | break; | ||
3161 | |||
3162 | default: | ||
3163 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
3164 | "Unhandled state `%s'\n", | ||
3165 | GNUNET_TRANSPORT_ps2s (n->state)); | ||
3166 | GNUNET_break (0); | ||
3167 | break; | ||
3168 | } | ||
3169 | delay = GNUNET_TIME_absolute_get_remaining (n->timeout); | ||
3170 | if ((GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) || | ||
3171 | (GNUNET_TRANSPORT_PS_CONNECTED == n->state)) | ||
3172 | { | ||
3173 | /* if we are *now* in one of the two states, we're sending | ||
3174 | keep alive messages, so we need to consider the keepalive | ||
3175 | delay, not just the connection timeout */ | ||
3176 | delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining ( | ||
3177 | n->keep_alive_time), | ||
3178 | delay); | ||
3179 | } | ||
3180 | if (NULL == n->task) | ||
3181 | n->task = GNUNET_SCHEDULER_add_delayed (delay, | ||
3182 | &master_task, | ||
3183 | n); | ||
3184 | } | ||
3185 | |||
3186 | |||
3187 | /** | ||
3188 | * Send a ACK message to the neighbour to confirm that we | ||
3189 | * got its SYN_ACK. | ||
3190 | * | ||
3191 | * @param n neighbour to send the ACK to | ||
3192 | */ | ||
3193 | static void | ||
3194 | send_session_ack_message (struct NeighbourMapEntry *n) | ||
3195 | { | ||
3196 | struct GNUNET_MessageHeader msg; | ||
3197 | |||
3198 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3199 | "Sending ACK message to peer `%s'\n", | ||
3200 | GNUNET_i2s (&n->id)); | ||
3201 | |||
3202 | msg.size = htons (sizeof(struct GNUNET_MessageHeader)); | ||
3203 | msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK); | ||
3204 | (void) send_with_session (n, | ||
3205 | &msg, | ||
3206 | sizeof(struct GNUNET_MessageHeader), | ||
3207 | UINT32_MAX, | ||
3208 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
3209 | GNUNET_NO, | ||
3210 | NULL, NULL); | ||
3211 | } | ||
3212 | |||
3213 | |||
3214 | /** | ||
3215 | * We received a 'SESSION_SYN_ACK' message from the other peer. | ||
3216 | * Consider switching to it. | ||
3217 | * | ||
3218 | * @param message possibly a `struct GNUNET_ATS_SessionConnectMessage` (check format) | ||
3219 | * @param peer identity of the peer to switch the address for | ||
3220 | * @param address address of the other peer, NULL if other peer | ||
3221 | * connected to us | ||
3222 | * @param session session to use (or NULL) | ||
3223 | * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error | ||
3224 | */ | ||
3225 | int | ||
3226 | GST_neighbours_handle_session_syn_ack (const struct | ||
3227 | GNUNET_MessageHeader *message, | ||
3228 | const struct | ||
3229 | GNUNET_HELLO_Address *address, | ||
3230 | struct GNUNET_ATS_Session *session) | ||
3231 | { | ||
3232 | const struct TransportSynMessage *scm; | ||
3233 | struct GNUNET_TIME_Absolute ts; | ||
3234 | struct NeighbourMapEntry *n; | ||
3235 | |||
3236 | (void) session; | ||
3237 | if (ntohs (message->size) != sizeof(struct TransportSynMessage)) | ||
3238 | { | ||
3239 | GNUNET_break_op (0); | ||
3240 | return GNUNET_SYSERR; | ||
3241 | } | ||
3242 | GNUNET_STATISTICS_update (GST_stats, | ||
3243 | gettext_noop | ||
3244 | ("# SYN_ACK messages received"), | ||
3245 | 1, GNUNET_NO); | ||
3246 | scm = (const struct TransportSynMessage *) message; | ||
3247 | GNUNET_break_op (ntohl (scm->reserved) == 0); | ||
3248 | if (NULL == (n = lookup_neighbour (&address->peer))) | ||
3249 | { | ||
3250 | GNUNET_STATISTICS_update (GST_stats, | ||
3251 | gettext_noop | ||
3252 | ("# unexpected SYN_ACK messages (no peer)"), | ||
3253 | 1, GNUNET_NO); | ||
3254 | return GNUNET_SYSERR; | ||
3255 | } | ||
3256 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3257 | "Received SYN_ACK message from peer `%s' in state %s/%s\n", | ||
3258 | GNUNET_i2s (&address->peer), | ||
3259 | GNUNET_TRANSPORT_ps2s (n->state), | ||
3260 | print_ack_state (n->ack_state)); | ||
3261 | ts = GNUNET_TIME_absolute_ntoh (scm->timestamp); | ||
3262 | switch (n->state) | ||
3263 | { | ||
3264 | case GNUNET_TRANSPORT_PS_NOT_CONNECTED: | ||
3265 | GNUNET_break (0); | ||
3266 | free_neighbour (n); | ||
3267 | return GNUNET_SYSERR; | ||
3268 | |||
3269 | case GNUNET_TRANSPORT_PS_INIT_ATS: | ||
3270 | GNUNET_STATISTICS_update (GST_stats, | ||
3271 | gettext_noop ( | ||
3272 | "# unexpected SYN_ACK messages (not ready)"), | ||
3273 | 1, | ||
3274 | GNUNET_NO); | ||
3275 | break; | ||
3276 | |||
3277 | case GNUNET_TRANSPORT_PS_SYN_SENT: | ||
3278 | if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us) | ||
3279 | { | ||
3280 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3281 | "SYN_ACK ignored as the timestamp does not match our SYN request\n"); | ||
3282 | return GNUNET_OK; | ||
3283 | } | ||
3284 | set_state_and_timeout (n, | ||
3285 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
3286 | GNUNET_TIME_relative_to_absolute ( | ||
3287 | GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT)); | ||
3288 | set_primary_address (n, | ||
3289 | n->primary_address.address, | ||
3290 | n->primary_address.session, | ||
3291 | n->primary_address.bandwidth_in, | ||
3292 | n->primary_address.bandwidth_out); | ||
3293 | send_session_ack_message (n); | ||
3294 | break; | ||
3295 | |||
3296 | case GNUNET_TRANSPORT_PS_SYN_RECV_ATS: | ||
3297 | case GNUNET_TRANSPORT_PS_SYN_RECV_ACK: | ||
3298 | GNUNET_STATISTICS_update (GST_stats, | ||
3299 | gettext_noop ( | ||
3300 | "# unexpected SYN_ACK messages (not ready)"), | ||
3301 | 1, | ||
3302 | GNUNET_NO); | ||
3303 | break; | ||
3304 | |||
3305 | case GNUNET_TRANSPORT_PS_CONNECTED: | ||
3306 | /* duplicate SYN_ACK, let's answer by duplicate ACK just in case */ | ||
3307 | send_session_ack_message (n); | ||
3308 | break; | ||
3309 | |||
3310 | case GNUNET_TRANSPORT_PS_RECONNECT_ATS: | ||
3311 | /* we didn't expect any SYN_ACK, as we are waiting for ATS | ||
3312 | to give us a new address... */ | ||
3313 | GNUNET_STATISTICS_update (GST_stats, | ||
3314 | gettext_noop ( | ||
3315 | "# unexpected SYN_ACK messages (waiting on ATS)"), | ||
3316 | 1, | ||
3317 | GNUNET_NO); | ||
3318 | break; | ||
3319 | |||
3320 | case GNUNET_TRANSPORT_PS_RECONNECT_SENT: | ||
3321 | /* Reconnecting with new address address worked; go back to connected! */ | ||
3322 | set_state_and_timeout (n, | ||
3323 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
3324 | GNUNET_TIME_relative_to_absolute ( | ||
3325 | GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT)); | ||
3326 | send_session_ack_message (n); | ||
3327 | break; | ||
3328 | |||
3329 | case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: | ||
3330 | /* new address worked; adopt it and go back to connected! */ | ||
3331 | set_state_and_timeout (n, | ||
3332 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
3333 | GNUNET_TIME_relative_to_absolute ( | ||
3334 | GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT)); | ||
3335 | GNUNET_break (GNUNET_NO == n->alternative_address.ats_active); | ||
3336 | |||
3337 | /* Set primary addresses */ | ||
3338 | set_primary_address (n, | ||
3339 | n->alternative_address.address, | ||
3340 | n->alternative_address.session, | ||
3341 | n->alternative_address.bandwidth_in, | ||
3342 | n->alternative_address.bandwidth_out); | ||
3343 | GNUNET_STATISTICS_update (GST_stats, | ||
3344 | gettext_noop ( | ||
3345 | "# Successful attempts to switch addresses"), | ||
3346 | 1, | ||
3347 | GNUNET_NO); | ||
3348 | |||
3349 | GNUNET_HELLO_address_free (n->alternative_address.address); | ||
3350 | memset (&n->alternative_address, | ||
3351 | 0, | ||
3352 | sizeof(n->alternative_address)); | ||
3353 | send_session_ack_message (n); | ||
3354 | break; | ||
3355 | |||
3356 | case GNUNET_TRANSPORT_PS_DISCONNECT: | ||
3357 | GNUNET_STATISTICS_update (GST_stats, | ||
3358 | gettext_noop | ||
3359 | ("# unexpected SYN_ACK messages (disconnecting)"), | ||
3360 | 1, GNUNET_NO); | ||
3361 | return GNUNET_SYSERR; | ||
3362 | |||
3363 | case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED: | ||
3364 | GNUNET_assert (0); | ||
3365 | break; | ||
3366 | |||
3367 | default: | ||
3368 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
3369 | "Unhandled state `%s'\n", | ||
3370 | GNUNET_TRANSPORT_ps2s (n->state)); | ||
3371 | GNUNET_break (0); | ||
3372 | return GNUNET_SYSERR; | ||
3373 | } | ||
3374 | return GNUNET_OK; | ||
3375 | } | ||
3376 | |||
3377 | |||
3378 | /** | ||
3379 | * A session was terminated. Take note; if needed, try to get | ||
3380 | * an alternative address from ATS. | ||
3381 | * | ||
3382 | * @param peer identity of the peer where the session died | ||
3383 | * @param session session that is gone | ||
3384 | * @return #GNUNET_YES if this was a session used, #GNUNET_NO if | ||
3385 | * this session was not in use | ||
3386 | */ | ||
3387 | int | ||
3388 | GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer, | ||
3389 | struct GNUNET_ATS_Session *session) | ||
3390 | { | ||
3391 | struct NeighbourMapEntry *n; | ||
3392 | |||
3393 | if (NULL == (n = lookup_neighbour (peer))) | ||
3394 | return GNUNET_NO; /* can't affect us */ | ||
3395 | if (session != n->primary_address.session) | ||
3396 | { | ||
3397 | /* Free alternative address */ | ||
3398 | if (session == n->alternative_address.session) | ||
3399 | { | ||
3400 | if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) | ||
3401 | set_state_and_timeout (n, | ||
3402 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
3403 | n->timeout); | ||
3404 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3405 | "Session died, cleaning up alternative address\n"); | ||
3406 | free_address (&n->alternative_address); | ||
3407 | } | ||
3408 | return GNUNET_NO; /* doesn't affect us further */ | ||
3409 | } | ||
3410 | |||
3411 | n->expect_latency_response = GNUNET_NO; | ||
3412 | /* The session for neighbour's primary address died */ | ||
3413 | switch (n->state) | ||
3414 | { | ||
3415 | case GNUNET_TRANSPORT_PS_NOT_CONNECTED: | ||
3416 | GNUNET_break (0); | ||
3417 | free_neighbour (n); | ||
3418 | return GNUNET_YES; | ||
3419 | |||
3420 | case GNUNET_TRANSPORT_PS_INIT_ATS: | ||
3421 | GNUNET_break (0); | ||
3422 | free_neighbour (n); | ||
3423 | return GNUNET_YES; | ||
3424 | |||
3425 | case GNUNET_TRANSPORT_PS_SYN_SENT: | ||
3426 | /* The session used to send the SYN terminated: | ||
3427 | * this implies a connect error*/ | ||
3428 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3429 | "Failed to send SYN in CONNECT_SENT with `%s' %p: session terminated\n", | ||
3430 | GST_plugins_a2s (n->primary_address.address), | ||
3431 | n->primary_address.session); | ||
3432 | |||
3433 | /* Destroy the address since it cannot be used */ | ||
3434 | unset_primary_address (n); | ||
3435 | set_state_and_timeout (n, | ||
3436 | GNUNET_TRANSPORT_PS_INIT_ATS, | ||
3437 | GNUNET_TIME_relative_to_absolute ( | ||
3438 | ATS_RESPONSE_TIMEOUT)); | ||
3439 | break; | ||
3440 | |||
3441 | case GNUNET_TRANSPORT_PS_SYN_RECV_ATS: | ||
3442 | case GNUNET_TRANSPORT_PS_SYN_RECV_ACK: | ||
3443 | /* error on inbound session; free neighbour entirely */ | ||
3444 | free_neighbour (n); | ||
3445 | return GNUNET_YES; | ||
3446 | |||
3447 | case GNUNET_TRANSPORT_PS_CONNECTED: | ||
3448 | /* Our primary connection died, try a fast reconnect */ | ||
3449 | unset_primary_address (n); | ||
3450 | set_state_and_timeout (n, | ||
3451 | GNUNET_TRANSPORT_PS_RECONNECT_ATS, | ||
3452 | GNUNET_TIME_relative_to_absolute ( | ||
3453 | ATS_RESPONSE_TIMEOUT)); | ||
3454 | break; | ||
3455 | |||
3456 | case GNUNET_TRANSPORT_PS_RECONNECT_ATS: | ||
3457 | /* we don't have an address, how can it go down? */ | ||
3458 | GNUNET_break (0); | ||
3459 | break; | ||
3460 | |||
3461 | case GNUNET_TRANSPORT_PS_RECONNECT_SENT: | ||
3462 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3463 | "Failed to send SYN in RECONNECT_SENT with `%s' %p: session terminated\n", | ||
3464 | GST_plugins_a2s (n->primary_address.address), | ||
3465 | n->primary_address.session); | ||
3466 | /* Destroy the address since it cannot be used */ | ||
3467 | unset_primary_address (n); | ||
3468 | set_state_and_timeout (n, | ||
3469 | GNUNET_TRANSPORT_PS_RECONNECT_ATS, | ||
3470 | GNUNET_TIME_relative_to_absolute ( | ||
3471 | ATS_RESPONSE_TIMEOUT)); | ||
3472 | break; | ||
3473 | |||
3474 | case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: | ||
3475 | /* primary went down while we were waiting for SYN_ACK on secondary; | ||
3476 | secondary as primary */ | ||
3477 | |||
3478 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3479 | "Connection `%s' %p to peer `%s' was terminated while switching, " | ||
3480 | "switching to alternative address `%s' %p\n", | ||
3481 | GST_plugins_a2s (n->primary_address.address), | ||
3482 | n->primary_address.session, | ||
3483 | GNUNET_i2s (peer), | ||
3484 | GST_plugins_a2s (n->alternative_address.address), | ||
3485 | n->alternative_address.session); | ||
3486 | |||
3487 | /* Destroy the inbound address since it cannot be used */ | ||
3488 | free_address (&n->primary_address); | ||
3489 | n->primary_address = n->alternative_address; | ||
3490 | GNUNET_assert (GNUNET_YES == | ||
3491 | GST_ats_is_known (n->primary_address.address, | ||
3492 | n->primary_address.session)); | ||
3493 | memset (&n->alternative_address, | ||
3494 | 0, | ||
3495 | sizeof(struct NeighbourAddress)); | ||
3496 | set_state_and_timeout (n, | ||
3497 | GNUNET_TRANSPORT_PS_RECONNECT_SENT, | ||
3498 | GNUNET_TIME_relative_to_absolute ( | ||
3499 | FAST_RECONNECT_TIMEOUT)); | ||
3500 | break; | ||
3501 | |||
3502 | case GNUNET_TRANSPORT_PS_DISCONNECT: | ||
3503 | unset_primary_address (n); | ||
3504 | break; | ||
3505 | |||
3506 | case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED: | ||
3507 | /* neighbour was freed and plugins told to terminate session */ | ||
3508 | return GNUNET_NO; | ||
3509 | |||
3510 | default: | ||
3511 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
3512 | "Unhandled state `%s'\n", | ||
3513 | GNUNET_TRANSPORT_ps2s (n->state)); | ||
3514 | GNUNET_break (0); | ||
3515 | break; | ||
3516 | } | ||
3517 | if (NULL != n->task) | ||
3518 | GNUNET_SCHEDULER_cancel (n->task); | ||
3519 | n->task = GNUNET_SCHEDULER_add_now (&master_task, n); | ||
3520 | return GNUNET_YES; | ||
3521 | } | ||
3522 | |||
3523 | |||
3524 | /** | ||
3525 | * We received a 'ACK' message from the other peer. | ||
3526 | * If we sent a 'SYN_ACK' last, this means we are now | ||
3527 | * connected. Otherwise, do nothing. | ||
3528 | * | ||
3529 | * @param message possibly a 'struct GNUNET_ATS_SessionConnectMessage' (check format) | ||
3530 | * @param address address of the other peer | ||
3531 | * @param session session to use (or NULL) | ||
3532 | * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error | ||
3533 | */ | ||
3534 | int | ||
3535 | GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message, | ||
3536 | const struct GNUNET_HELLO_Address *address, | ||
3537 | struct GNUNET_ATS_Session *session) | ||
3538 | { | ||
3539 | struct NeighbourMapEntry *n; | ||
3540 | |||
3541 | (void) session; | ||
3542 | if (ntohs (message->size) != sizeof(struct GNUNET_MessageHeader)) | ||
3543 | { | ||
3544 | GNUNET_break_op (0); | ||
3545 | return GNUNET_SYSERR; | ||
3546 | } | ||
3547 | GNUNET_STATISTICS_update (GST_stats, | ||
3548 | gettext_noop ("# ACK messages received"), | ||
3549 | 1, | ||
3550 | GNUNET_NO); | ||
3551 | if (NULL == (n = lookup_neighbour (&address->peer))) | ||
3552 | { | ||
3553 | GNUNET_break_op (0); | ||
3554 | return GNUNET_SYSERR; | ||
3555 | } | ||
3556 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3557 | "Received ACK for peer `%s' in state %s/%s\n", | ||
3558 | GNUNET_i2s (&address->peer), | ||
3559 | GNUNET_TRANSPORT_ps2s (n->state), | ||
3560 | print_ack_state (n->ack_state)); | ||
3561 | |||
3562 | /* Check if we are in a plausible state for having sent | ||
3563 | a SYN_ACK. If not, return, otherwise break. | ||
3564 | |||
3565 | The remote peers sends a ACK as a response for a SYN_ACK | ||
3566 | message. | ||
3567 | |||
3568 | We expect a ACK: | ||
3569 | - If a remote peer has sent a SYN, we responded with a SYN_ACK and | ||
3570 | now wait for the ACK to finally be connected | ||
3571 | - If we sent a SYN_ACK to this peer before */if (((GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state) && | ||
3572 | (ACK_SEND_ACK != n->ack_state)) || | ||
3573 | (NULL == n->primary_address.address)) | ||
3574 | { | ||
3575 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
3576 | "Received unexpected ACK message from peer `%s' in state %s/%s\n", | ||
3577 | GNUNET_i2s (&address->peer), | ||
3578 | GNUNET_TRANSPORT_ps2s (n->state), | ||
3579 | print_ack_state (n->ack_state)); | ||
3580 | |||
3581 | GNUNET_STATISTICS_update (GST_stats, | ||
3582 | gettext_noop ("# unexpected ACK messages"), | ||
3583 | 1, | ||
3584 | GNUNET_NO); | ||
3585 | return GNUNET_OK; | ||
3586 | } | ||
3587 | if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) | ||
3588 | { | ||
3589 | /* We tried to switch addresses while being connect. We explicitly wait | ||
3590 | * for a SYN_ACK before going to GNUNET_TRANSPORT_PS_CONNECTED, | ||
3591 | * so we do not want to set the address as in use! */ | ||
3592 | return GNUNET_OK; | ||
3593 | } | ||
3594 | set_state_and_timeout (n, | ||
3595 | GNUNET_TRANSPORT_PS_CONNECTED, | ||
3596 | GNUNET_TIME_relative_to_absolute ( | ||
3597 | GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT)); | ||
3598 | |||
3599 | if (NULL == n->primary_address.address) | ||
3600 | { | ||
3601 | /* See issue #3693. | ||
3602 | * We are in state = PSY_SYN_RECV_ACK or ack_state = ACK_SEND_ACK, which | ||
3603 | * really means we did try (and succeed) to send a SYN and are waiting for | ||
3604 | * an ACK. | ||
3605 | * That suggests that the primary_address used to be non-NULL, but maybe it | ||
3606 | * got reset to NULL without the state being changed appropriately? | ||
3607 | */GNUNET_break (0); | ||
3608 | return GNUNET_OK; | ||
3609 | } | ||
3610 | |||
3611 | /* Reset backoff for primary address */ | ||
3612 | GST_ats_block_reset (n->primary_address.address, | ||
3613 | n->primary_address.session); | ||
3614 | return GNUNET_OK; | ||
3615 | } | ||
3616 | |||
3617 | |||
3618 | /** | ||
3619 | * Test if we're connected to the given peer. | ||
3620 | * | ||
3621 | * @param target peer to test | ||
3622 | * @return #GNUNET_YES if we are connected, #GNUNET_NO if not | ||
3623 | */ | ||
3624 | int | ||
3625 | GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target) | ||
3626 | { | ||
3627 | return test_connected (lookup_neighbour (target)); | ||
3628 | } | ||
3629 | |||
3630 | |||
3631 | /** | ||
3632 | * Task to asynchronously run #free_neighbour(). | ||
3633 | * | ||
3634 | * @param cls the `struct NeighbourMapEntry` to free | ||
3635 | */ | ||
3636 | static void | ||
3637 | delayed_disconnect (void *cls) | ||
3638 | { | ||
3639 | struct NeighbourMapEntry *n = cls; | ||
3640 | |||
3641 | n->delayed_disconnect_task = NULL; | ||
3642 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3643 | "Disconnecting by request from peer %s\n", | ||
3644 | GNUNET_i2s (&n->id)); | ||
3645 | free_neighbour (n); | ||
3646 | } | ||
3647 | |||
3648 | |||
3649 | /** | ||
3650 | * We received a quota message from the given peer, | ||
3651 | * validate and process. | ||
3652 | * | ||
3653 | * @param peer sender of the message | ||
3654 | * @param msg the quota message | ||
3655 | */ | ||
3656 | void | ||
3657 | GST_neighbours_handle_quota_message (const struct GNUNET_PeerIdentity *peer, | ||
3658 | const struct GNUNET_MessageHeader *msg) | ||
3659 | { | ||
3660 | struct NeighbourMapEntry *n; | ||
3661 | const struct GNUNET_ATS_SessionQuotaMessage *sqm; | ||
3662 | struct GNUNET_BANDWIDTH_Value32NBO last; | ||
3663 | |||
3664 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3665 | "Received QUOTA message from peer `%s'\n", | ||
3666 | GNUNET_i2s (peer)); | ||
3667 | if (ntohs (msg->size) != sizeof(struct GNUNET_ATS_SessionQuotaMessage)) | ||
3668 | { | ||
3669 | GNUNET_break_op (0); | ||
3670 | GNUNET_STATISTICS_update (GST_stats, | ||
3671 | gettext_noop ( | ||
3672 | "# quota messages ignored (malformed)"), | ||
3673 | 1, | ||
3674 | GNUNET_NO); | ||
3675 | return; | ||
3676 | } | ||
3677 | GNUNET_STATISTICS_update (GST_stats, | ||
3678 | gettext_noop | ||
3679 | ("# QUOTA messages received"), | ||
3680 | 1, GNUNET_NO); | ||
3681 | sqm = (const struct GNUNET_ATS_SessionQuotaMessage *) msg; | ||
3682 | if (NULL == (n = lookup_neighbour (peer))) | ||
3683 | { | ||
3684 | /* gone already */ | ||
3685 | return; | ||
3686 | } | ||
3687 | last = GNUNET_BANDWIDTH_value_max (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT, | ||
3688 | GNUNET_BANDWIDTH_value_init (ntohl ( | ||
3689 | sqm->quota))); | ||
3690 | if (last.value__ != n->neighbour_receive_quota.value__) | ||
3691 | { | ||
3692 | n->neighbour_receive_quota = last; | ||
3693 | send_outbound_quota_to_clients (n); | ||
3694 | } | ||
3695 | } | ||
3696 | |||
3697 | |||
3698 | /** | ||
3699 | * We received a disconnect message from the given peer, | ||
3700 | * validate and process. | ||
3701 | * | ||
3702 | * @param peer sender of the message | ||
3703 | * @param msg the disconnect message | ||
3704 | */ | ||
3705 | void | ||
3706 | GST_neighbours_handle_disconnect_message (const struct | ||
3707 | GNUNET_PeerIdentity *peer, | ||
3708 | const struct | ||
3709 | GNUNET_MessageHeader *msg) | ||
3710 | { | ||
3711 | struct NeighbourMapEntry *n; | ||
3712 | const struct GNUNET_ATS_SessionDisconnectMessage *sdm; | ||
3713 | |||
3714 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3715 | "Received DISCONNECT message from peer `%s'\n", | ||
3716 | GNUNET_i2s (peer)); | ||
3717 | if (ntohs (msg->size) != sizeof(struct GNUNET_ATS_SessionDisconnectMessage)) | ||
3718 | { | ||
3719 | GNUNET_break_op (0); | ||
3720 | GNUNET_STATISTICS_update (GST_stats, | ||
3721 | gettext_noop | ||
3722 | ("# disconnect messages ignored (malformed)"), | ||
3723 | 1, | ||
3724 | GNUNET_NO); | ||
3725 | return; | ||
3726 | } | ||
3727 | GNUNET_STATISTICS_update (GST_stats, | ||
3728 | gettext_noop | ||
3729 | ("# DISCONNECT messages received"), | ||
3730 | 1, GNUNET_NO); | ||
3731 | sdm = (const struct GNUNET_ATS_SessionDisconnectMessage *) msg; | ||
3732 | if (NULL == (n = lookup_neighbour (peer))) | ||
3733 | { | ||
3734 | /* gone already */ | ||
3735 | return; | ||
3736 | } | ||
3737 | if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= | ||
3738 | n->connect_ack_timestamp.abs_value_us) | ||
3739 | { | ||
3740 | GNUNET_STATISTICS_update (GST_stats, | ||
3741 | gettext_noop ( | ||
3742 | "# disconnect messages ignored (timestamp)"), | ||
3743 | 1, | ||
3744 | GNUNET_NO); | ||
3745 | return; | ||
3746 | } | ||
3747 | if (0 != memcmp (peer, | ||
3748 | &sdm->public_key, | ||
3749 | sizeof(struct GNUNET_PeerIdentity))) | ||
3750 | { | ||
3751 | GNUNET_break_op (0); | ||
3752 | return; | ||
3753 | } | ||
3754 | if (ntohl (sdm->purpose.size) != | ||
3755 | sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) | ||
3756 | + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) | ||
3757 | + sizeof(struct GNUNET_TIME_AbsoluteNBO)) | ||
3758 | { | ||
3759 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3760 | "DISCONNECT message from peer `%s' has invalid size\n", | ||
3761 | GNUNET_i2s (peer)); | ||
3762 | GNUNET_break_op (0); | ||
3763 | return; | ||
3764 | } | ||
3765 | if (GNUNET_OK != | ||
3766 | GNUNET_CRYPTO_eddsa_verify_ ( | ||
3767 | GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, | ||
3768 | &sdm->purpose, | ||
3769 | &sdm->signature, | ||
3770 | &sdm->public_key)) | ||
3771 | { | ||
3772 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3773 | "DISCONNECT message from peer `%s' cannot be verified \n", | ||
3774 | GNUNET_i2s (peer)); | ||
3775 | GNUNET_break_op (0); | ||
3776 | return; | ||
3777 | } | ||
3778 | if (NULL == n->delayed_disconnect_task) | ||
3779 | { | ||
3780 | n->delayed_disconnect_task = GNUNET_SCHEDULER_add_now (&delayed_disconnect, | ||
3781 | n); | ||
3782 | } | ||
3783 | } | ||
3784 | |||
3785 | |||
3786 | /** | ||
3787 | * Closure for the #neighbours_iterate() function. | ||
3788 | */ | ||
3789 | struct IteratorContext | ||
3790 | { | ||
3791 | /** | ||
3792 | * Function to call on each connected neighbour. | ||
3793 | */ | ||
3794 | GST_NeighbourIterator cb; | ||
3795 | |||
3796 | /** | ||
3797 | * Closure for @e cb. | ||
3798 | */ | ||
3799 | void *cb_cls; | ||
3800 | }; | ||
3801 | |||
3802 | |||
3803 | /** | ||
3804 | * Call the callback from the closure for each neighbour. | ||
3805 | * | ||
3806 | * @param cls the `struct IteratorContext` | ||
3807 | * @param key the hash of the public key of the neighbour | ||
3808 | * @param value the `struct NeighbourMapEntry` | ||
3809 | * @return #GNUNET_OK (continue to iterate) | ||
3810 | */ | ||
3811 | static int | ||
3812 | neighbours_iterate (void *cls, | ||
3813 | const struct GNUNET_PeerIdentity *key, | ||
3814 | void *value) | ||
3815 | { | ||
3816 | struct IteratorContext *ic = cls; | ||
3817 | struct NeighbourMapEntry *n = value; | ||
3818 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in; | ||
3819 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out; | ||
3820 | |||
3821 | (void) key; | ||
3822 | if (NULL != n->primary_address.address) | ||
3823 | { | ||
3824 | bandwidth_in = n->primary_address.bandwidth_in; | ||
3825 | bandwidth_out = n->primary_address.bandwidth_out; | ||
3826 | } | ||
3827 | else | ||
3828 | { | ||
3829 | bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT; | ||
3830 | bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT; | ||
3831 | } | ||
3832 | ic->cb (ic->cb_cls, | ||
3833 | &n->id, | ||
3834 | n->primary_address.address, | ||
3835 | n->state, | ||
3836 | n->timeout, | ||
3837 | bandwidth_in, bandwidth_out); | ||
3838 | return GNUNET_OK; | ||
3839 | } | ||
3840 | |||
3841 | |||
3842 | /** | ||
3843 | * Iterate over all connected neighbours. | ||
3844 | * | ||
3845 | * @param cb function to call | ||
3846 | * @param cb_cls closure for @a cb | ||
3847 | */ | ||
3848 | void | ||
3849 | GST_neighbours_iterate (GST_NeighbourIterator cb, | ||
3850 | void *cb_cls) | ||
3851 | { | ||
3852 | struct IteratorContext ic; | ||
3853 | |||
3854 | if (NULL == neighbours) | ||
3855 | return; /* can happen during shutdown */ | ||
3856 | ic.cb = cb; | ||
3857 | ic.cb_cls = cb_cls; | ||
3858 | GNUNET_CONTAINER_multipeermap_iterate (neighbours, | ||
3859 | &neighbours_iterate, | ||
3860 | &ic); | ||
3861 | } | ||
3862 | |||
3863 | |||
3864 | /** | ||
3865 | * If we have an active connection to the given target, it must be shutdown. | ||
3866 | * | ||
3867 | * @param target peer to disconnect from | ||
3868 | */ | ||
3869 | void | ||
3870 | GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target) | ||
3871 | { | ||
3872 | struct NeighbourMapEntry *n; | ||
3873 | |||
3874 | if (NULL == (n = lookup_neighbour (target))) | ||
3875 | return; /* not active */ | ||
3876 | if (GNUNET_YES == test_connected (n)) | ||
3877 | GNUNET_STATISTICS_update (GST_stats, | ||
3878 | gettext_noop ( | ||
3879 | "# disconnected from peer upon explicit request"), | ||
3880 | 1, | ||
3881 | GNUNET_NO); | ||
3882 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3883 | "Forced disconnect from peer %s\n", | ||
3884 | GNUNET_i2s (target)); | ||
3885 | disconnect_neighbour (n); | ||
3886 | } | ||
3887 | |||
3888 | |||
3889 | /** | ||
3890 | * Obtain current address information for the given neighbour. | ||
3891 | * | ||
3892 | * @param peer | ||
3893 | * @return address currently used | ||
3894 | */ | ||
3895 | const struct GNUNET_HELLO_Address * | ||
3896 | GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer) | ||
3897 | { | ||
3898 | struct NeighbourMapEntry *n; | ||
3899 | |||
3900 | n = lookup_neighbour (peer); | ||
3901 | if (NULL == n) | ||
3902 | return NULL; | ||
3903 | return n->primary_address.address; | ||
3904 | } | ||
3905 | |||
3906 | |||
3907 | /** | ||
3908 | * Initialize the neighbours subsystem. | ||
3909 | * | ||
3910 | * @param max_fds maximum number of fds to use | ||
3911 | */ | ||
3912 | void | ||
3913 | GST_neighbours_start (unsigned int max_fds) | ||
3914 | { | ||
3915 | (void) max_fds; | ||
3916 | neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, | ||
3917 | GNUNET_NO); | ||
3918 | util_transmission_tk = GNUNET_SCHEDULER_add_delayed ( | ||
3919 | UTIL_TRANSMISSION_INTERVAL, | ||
3920 | &utilization_transmission, | ||
3921 | NULL); | ||
3922 | } | ||
3923 | |||
3924 | |||
3925 | /** | ||
3926 | * Disconnect from the given neighbour. | ||
3927 | * | ||
3928 | * @param cls unused | ||
3929 | * @param key hash of neighbour's public key (not used) | ||
3930 | * @param value the `struct NeighbourMapEntry` of the neighbour | ||
3931 | * @return #GNUNET_OK (continue to iterate) | ||
3932 | */ | ||
3933 | static int | ||
3934 | disconnect_all_neighbours (void *cls, | ||
3935 | const struct GNUNET_PeerIdentity *key, | ||
3936 | void *value) | ||
3937 | { | ||
3938 | struct NeighbourMapEntry *n = value; | ||
3939 | |||
3940 | (void) cls; | ||
3941 | (void) key; | ||
3942 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3943 | "Disconnecting peer `%4s' during shutdown\n", | ||
3944 | GNUNET_i2s (&n->id)); | ||
3945 | free_neighbour (n); | ||
3946 | return GNUNET_OK; | ||
3947 | } | ||
3948 | |||
3949 | |||
3950 | /** | ||
3951 | * Cleanup the neighbours subsystem. | ||
3952 | */ | ||
3953 | void | ||
3954 | GST_neighbours_stop () | ||
3955 | { | ||
3956 | if (NULL == neighbours) | ||
3957 | return; | ||
3958 | if (NULL != util_transmission_tk) | ||
3959 | { | ||
3960 | GNUNET_SCHEDULER_cancel (util_transmission_tk); | ||
3961 | util_transmission_tk = NULL; | ||
3962 | } | ||
3963 | GNUNET_CONTAINER_multipeermap_iterate (neighbours, | ||
3964 | &disconnect_all_neighbours, | ||
3965 | NULL); | ||
3966 | GNUNET_CONTAINER_multipeermap_destroy (neighbours); | ||
3967 | neighbours = NULL; | ||
3968 | } | ||
3969 | |||
3970 | |||
3971 | /* end of file gnunet-service-transport_neighbours.c */ | ||