diff options
author | Christian Grothoff <christian@grothoff.org> | 2011-10-05 13:26:24 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2011-10-05 13:26:24 +0000 |
commit | 0f29195adbd56ae10dea70c2951333c13e765f88 (patch) | |
tree | 83247d0f38a2cba50209af2325bad3d74890eb71 /src | |
parent | f39c4e7141b1fbb4830cb24ff630a879337f98d4 (diff) | |
download | gnunet-0f29195adbd56ae10dea70c2951333c13e765f88.tar.gz gnunet-0f29195adbd56ae10dea70c2951333c13e765f88.zip |
towards new core service implementation -- breaking core up into smaller modules
Diffstat (limited to 'src')
-rw-r--r-- | src/core/gnunet-service-core-new.c | 305 | ||||
-rw-r--r-- | src/core/gnunet-service-core.h | 15 | ||||
-rw-r--r-- | src/core/gnunet-service-core_ats.c | 159 | ||||
-rw-r--r-- | src/core/gnunet-service-core_clients.c | 1102 | ||||
-rw-r--r-- | src/core/gnunet-service-core_crypto.c | 361 | ||||
-rw-r--r-- | src/core/gnunet-service-core_extern.c | 39 | ||||
-rw-r--r-- | src/core/gnunet-service-core_kx.c | 958 | ||||
-rw-r--r-- | src/core/gnunet-service-core_kx.h | 77 | ||||
-rw-r--r-- | src/core/gnunet-service-core_neighbours.c | 617 | ||||
-rw-r--r-- | src/core/gnunet-service-core_plan.c | 563 | ||||
-rw-r--r-- | src/core/gnunet-service-core_sessions.c | 713 | ||||
-rw-r--r-- | src/core/gnunet-service-core_typemap.c | 96 |
12 files changed, 5005 insertions, 0 deletions
diff --git a/src/core/gnunet-service-core-new.c b/src/core/gnunet-service-core-new.c new file mode 100644 index 000000000..cebc5237b --- /dev/null +++ b/src/core/gnunet-service-core-new.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2009, 2010 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | 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 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core.c | ||
23 | * @brief high-level P2P messaging | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * Type map implementation: | ||
27 | * - track type maps for neighbours (can wait) | ||
28 | * - only notify clients about peers with matching type maps (can wait) | ||
29 | * | ||
30 | * Considerations for later: | ||
31 | * - check that hostkey used by transport (for HELLOs) is the | ||
32 | * same as the hostkey that we are using! | ||
33 | */ | ||
34 | #include "platform.h" | ||
35 | #include <zlib.h> | ||
36 | #include "gnunet_constants.h" | ||
37 | #include "gnunet_util_lib.h" | ||
38 | #include "gnunet_hello_lib.h" | ||
39 | #include "gnunet_peerinfo_service.h" | ||
40 | #include "gnunet_protocols.h" | ||
41 | #include "gnunet_signatures.h" | ||
42 | #include "gnunet_statistics_service.h" | ||
43 | #include "gnunet_transport_service.h" | ||
44 | #include "core.h" | ||
45 | |||
46 | |||
47 | #define DEBUG_HANDSHAKE GNUNET_EXTRA_LOGGING | ||
48 | |||
49 | #define DEBUG_CORE_QUOTA GNUNET_EXTRA_LOGGING | ||
50 | |||
51 | /** | ||
52 | * Receive and send buffer windows grow over time. For | ||
53 | * how long can 'unused' bandwidth accumulate before we | ||
54 | * need to cap it? (specified in seconds). | ||
55 | */ | ||
56 | #define MAX_WINDOW_TIME_S (5 * 60) | ||
57 | |||
58 | /** | ||
59 | * How many messages do we queue up at most for optional | ||
60 | * notifications to a client? (this can cause notifications | ||
61 | * about outgoing messages to be dropped). | ||
62 | */ | ||
63 | #define MAX_NOTIFY_QUEUE 1024 | ||
64 | |||
65 | /** | ||
66 | * Minimum bandwidth (out) to assign to any connected peer. | ||
67 | * Should be rather low; values larger than DEFAULT_BW_IN_OUT make no | ||
68 | * sense. | ||
69 | */ | ||
70 | #define MIN_BANDWIDTH_PER_PEER GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT | ||
71 | |||
72 | /** | ||
73 | * After how much time past the "official" expiration time do | ||
74 | * we discard messages? Should not be zero since we may | ||
75 | * intentionally defer transmission until close to the deadline | ||
76 | * and then may be slightly past the deadline due to inaccuracy | ||
77 | * in sleep and our own CPU consumption. | ||
78 | */ | ||
79 | #define PAST_EXPIRATION_DISCARD_TIME GNUNET_TIME_UNIT_SECONDS | ||
80 | |||
81 | /** | ||
82 | * What is the maximum delay for a SET_KEY message? | ||
83 | */ | ||
84 | #define MAX_SET_KEY_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) | ||
85 | |||
86 | /** | ||
87 | * How long do we wait for SET_KEY confirmation initially? | ||
88 | */ | ||
89 | #define INITIAL_SET_KEY_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (MAX_SET_KEY_DELAY, 1) | ||
90 | |||
91 | /** | ||
92 | * What is the maximum delay for a PING message? | ||
93 | */ | ||
94 | #define MAX_PING_DELAY GNUNET_TIME_relative_multiply (MAX_SET_KEY_DELAY, 2) | ||
95 | |||
96 | /** | ||
97 | * What is the maximum delay for a PONG message? | ||
98 | */ | ||
99 | #define MAX_PONG_DELAY GNUNET_TIME_relative_multiply (MAX_PING_DELAY, 2) | ||
100 | |||
101 | /** | ||
102 | * What is the minimum frequency for a PING message? | ||
103 | */ | ||
104 | #define MIN_PING_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
105 | |||
106 | /** | ||
107 | * How often do we recalculate bandwidth quotas? | ||
108 | */ | ||
109 | #define QUOTA_UPDATE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
110 | |||
111 | /** | ||
112 | * What is the priority for a SET_KEY message? | ||
113 | */ | ||
114 | #define SET_KEY_PRIORITY 0xFFFFFF | ||
115 | |||
116 | /** | ||
117 | * What is the priority for a PING message? | ||
118 | */ | ||
119 | #define PING_PRIORITY 0xFFFFFF | ||
120 | |||
121 | /** | ||
122 | * What is the priority for a PONG message? | ||
123 | */ | ||
124 | #define PONG_PRIORITY 0xFFFFFF | ||
125 | |||
126 | /** | ||
127 | * How many messages do we queue per peer at most? Must be at | ||
128 | * least two. | ||
129 | */ | ||
130 | #define MAX_PEER_QUEUE_SIZE 16 | ||
131 | |||
132 | /** | ||
133 | * How many non-mandatory messages do we queue per client at most? | ||
134 | */ | ||
135 | #define MAX_CLIENT_QUEUE_SIZE 32 | ||
136 | |||
137 | /** | ||
138 | * What is the maximum age of a message for us to consider | ||
139 | * processing it? Note that this looks at the timestamp used | ||
140 | * by the other peer, so clock skew between machines does | ||
141 | * come into play here. So this should be picked high enough | ||
142 | * so that a little bit of clock skew does not prevent peers | ||
143 | * from connecting to us. | ||
144 | */ | ||
145 | #define MAX_MESSAGE_AGE GNUNET_TIME_UNIT_DAYS | ||
146 | |||
147 | |||
148 | /** | ||
149 | * State machine for our P2P encryption handshake. Everyone starts in | ||
150 | * "DOWN", if we receive the other peer's key (other peer initiated) | ||
151 | * we start in state RECEIVED (since we will immediately send our | ||
152 | * own); otherwise we start in SENT. If we get back a PONG from | ||
153 | * within either state, we move up to CONFIRMED (the PONG will always | ||
154 | * be sent back encrypted with the key we sent to the other peer). | ||
155 | */ | ||
156 | enum PeerStateMachine | ||
157 | { | ||
158 | /** | ||
159 | * No handshake yet. | ||
160 | */ | ||
161 | PEER_STATE_DOWN, | ||
162 | |||
163 | /** | ||
164 | * We've sent our session key. | ||
165 | */ | ||
166 | PEER_STATE_KEY_SENT, | ||
167 | |||
168 | /** | ||
169 | * We've received the other peers session key. | ||
170 | */ | ||
171 | PEER_STATE_KEY_RECEIVED, | ||
172 | |||
173 | /** | ||
174 | * The other peer has confirmed our session key with a message | ||
175 | * encrypted with his session key (which we got). Session is now fully up. | ||
176 | */ | ||
177 | PEER_STATE_KEY_CONFIRMED | ||
178 | }; | ||
179 | |||
180 | |||
181 | /** | ||
182 | * Encapsulation for encrypted messages exchanged between | ||
183 | * peers. Followed by the actual encrypted data. | ||
184 | */ | ||
185 | struct EncryptedMessage | ||
186 | { | ||
187 | /** | ||
188 | * Message type is either CORE_ENCRYPTED_MESSAGE. | ||
189 | */ | ||
190 | struct GNUNET_MessageHeader header; | ||
191 | |||
192 | /** | ||
193 | * Random value used for IV generation. | ||
194 | */ | ||
195 | uint32_t iv_seed GNUNET_PACKED; | ||
196 | |||
197 | /** | ||
198 | * MAC of the encrypted message (starting at 'sequence_number'), | ||
199 | * used to verify message integrity. Everything after this value | ||
200 | * (excluding this value itself) will be encrypted and authenticated. | ||
201 | * ENCRYPTED_HEADER_SIZE must be set to the offset of the *next* field. | ||
202 | */ | ||
203 | GNUNET_HashCode hmac; | ||
204 | |||
205 | /** | ||
206 | * Sequence number, in network byte order. This field | ||
207 | * must be the first encrypted/decrypted field | ||
208 | */ | ||
209 | uint32_t sequence_number GNUNET_PACKED; | ||
210 | |||
211 | /** | ||
212 | * Desired bandwidth (how much we should send to this peer / how | ||
213 | * much is the sender willing to receive)? | ||
214 | */ | ||
215 | struct GNUNET_BANDWIDTH_Value32NBO inbound_bw_limit; | ||
216 | |||
217 | /** | ||
218 | * Timestamp. Used to prevent reply of ancient messages | ||
219 | * (recent messages are caught with the sequence number). | ||
220 | */ | ||
221 | struct GNUNET_TIME_AbsoluteNBO timestamp; | ||
222 | |||
223 | }; | ||
224 | |||
225 | |||
226 | /** | ||
227 | * Number of bytes (at the beginning) of "struct EncryptedMessage" | ||
228 | * that are NOT encrypted. | ||
229 | */ | ||
230 | #define ENCRYPTED_HEADER_SIZE (offsetof(struct EncryptedMessage, sequence_number)) | ||
231 | |||
232 | |||
233 | /** | ||
234 | * Our identity. | ||
235 | */ | ||
236 | struct GNUNET_PeerIdentity GSC_my_identity; | ||
237 | |||
238 | /** | ||
239 | * Our configuration. | ||
240 | */ | ||
241 | const struct GNUNET_CONFIGURATION_Handle *GSC_cfg; | ||
242 | |||
243 | /** | ||
244 | * For creating statistics. | ||
245 | */ | ||
246 | struct GNUNET_STATISTICS_Handle *GSC_stats; | ||
247 | |||
248 | |||
249 | |||
250 | |||
251 | |||
252 | /** | ||
253 | * Last task run during shutdown. Disconnects us from | ||
254 | * the transport. | ||
255 | */ | ||
256 | static void | ||
257 | cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
258 | { | ||
259 | #if DEBUG_CORE | ||
260 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service shutting down.\n"); | ||
261 | #endif | ||
262 | if (stats != NULL) | ||
263 | GNUNET_STATISTICS_destroy (stats, GNUNET_NO); | ||
264 | } | ||
265 | |||
266 | |||
267 | /** | ||
268 | * Initiate core service. | ||
269 | * | ||
270 | * @param cls closure | ||
271 | * @param server the initialized server | ||
272 | * @param c configuration to use | ||
273 | */ | ||
274 | static void | ||
275 | run (void *cls, struct GNUNET_SERVER_Handle *server, | ||
276 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
277 | { | ||
278 | cfg = c; | ||
279 | /* setup transport connection */ | ||
280 | stats = GNUNET_STATISTICS_create ("core", cfg); | ||
281 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleaning_task, | ||
282 | NULL); | ||
283 | /* process client requests */ | ||
284 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Core service of `%4s' ready.\n"), | ||
285 | GNUNET_i2s (&my_identity)); | ||
286 | } | ||
287 | |||
288 | |||
289 | |||
290 | /** | ||
291 | * The main function for the transport service. | ||
292 | * | ||
293 | * @param argc number of arguments from the command line | ||
294 | * @param argv command line arguments | ||
295 | * @return 0 ok, 1 on error | ||
296 | */ | ||
297 | int | ||
298 | main (int argc, char *const *argv) | ||
299 | { | ||
300 | return (GNUNET_OK == | ||
301 | GNUNET_SERVICE_run (argc, argv, "core", GNUNET_SERVICE_OPTION_NONE, | ||
302 | &run, NULL)) ? 0 : 1; | ||
303 | } | ||
304 | |||
305 | /* end of gnunet-service-core.c */ | ||
diff --git a/src/core/gnunet-service-core.h b/src/core/gnunet-service-core.h new file mode 100644 index 000000000..d98b318e5 --- /dev/null +++ b/src/core/gnunet-service-core.h | |||
@@ -0,0 +1,15 @@ | |||
1 | |||
2 | /** | ||
3 | * Our configuration. | ||
4 | */ | ||
5 | extern const struct GNUNET_CONFIGURATION_Handle *GSC_cfg; | ||
6 | |||
7 | /** | ||
8 | * For creating statistics. | ||
9 | */ | ||
10 | extern struct GNUNET_STATISTICS_Handle *GSC_stats; | ||
11 | |||
12 | /** | ||
13 | * Our identity. | ||
14 | */ | ||
15 | extern struct GNUNET_PeerIdentity GSC_my_identity; | ||
diff --git a/src/core/gnunet-service-core_ats.c b/src/core/gnunet-service-core_ats.c new file mode 100644 index 000000000..3be2da205 --- /dev/null +++ b/src/core/gnunet-service-core_ats.c | |||
@@ -0,0 +1,159 @@ | |||
1 | |||
2 | /** | ||
3 | * How much inbound bandwidth are we supposed to be using per second? | ||
4 | */ | ||
5 | static unsigned long long bandwidth_target_in_bps; | ||
6 | |||
7 | /** | ||
8 | * How much outbound bandwidth are we supposed to be using per second? | ||
9 | */ | ||
10 | static unsigned long long bandwidth_target_out_bps; | ||
11 | |||
12 | |||
13 | |||
14 | /** | ||
15 | * Schedule the task that will recalculate the bandwidth | ||
16 | * quota for this peer (and possibly force a disconnect of | ||
17 | * idle peers by calculating a bandwidth of zero). | ||
18 | */ | ||
19 | static void | ||
20 | schedule_quota_update (struct Neighbour *n) | ||
21 | { | ||
22 | GNUNET_assert (n->quota_update_task == GNUNET_SCHEDULER_NO_TASK); | ||
23 | n->quota_update_task = | ||
24 | GNUNET_SCHEDULER_add_delayed (QUOTA_UPDATE_FREQUENCY, | ||
25 | &neighbour_quota_update, n); | ||
26 | } | ||
27 | |||
28 | |||
29 | /** | ||
30 | * Function that recalculates the bandwidth quota for the | ||
31 | * given neighbour and transmits it to the transport service. | ||
32 | * | ||
33 | * @param cls neighbour for the quota update | ||
34 | * @param tc context | ||
35 | */ | ||
36 | static void | ||
37 | neighbour_quota_update (void *cls, | ||
38 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
39 | { | ||
40 | struct Neighbour *n = cls; | ||
41 | struct GNUNET_BANDWIDTH_Value32NBO q_in; | ||
42 | struct GNUNET_BANDWIDTH_Value32NBO q_out; | ||
43 | struct GNUNET_BANDWIDTH_Value32NBO q_out_min; | ||
44 | double pref_rel; | ||
45 | double share; | ||
46 | unsigned long long distributable; | ||
47 | uint64_t need_per_peer; | ||
48 | uint64_t need_per_second; | ||
49 | unsigned int neighbour_count; | ||
50 | |||
51 | #if DEBUG_CORE > 1 | ||
52 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
53 | "Neighbour quota update calculation running for peer `%4s'\n", | ||
54 | GNUNET_i2s (&n->peer)); | ||
55 | #endif | ||
56 | n->quota_update_task = GNUNET_SCHEDULER_NO_TASK; | ||
57 | /* calculate relative preference among all neighbours; | ||
58 | * divides by a bit more to avoid division by zero AND to | ||
59 | * account for possibility of new neighbours joining any time | ||
60 | * AND to convert to double... */ | ||
61 | neighbour_count = GNUNET_CONTAINER_multihashmap_size (neighbours); | ||
62 | if (neighbour_count == 0) | ||
63 | return; | ||
64 | if (preference_sum == 0) | ||
65 | { | ||
66 | pref_rel = 1.0 / (double) neighbour_count; | ||
67 | } | ||
68 | else | ||
69 | { | ||
70 | pref_rel = (double) n->current_preference / preference_sum; | ||
71 | } | ||
72 | need_per_peer = | ||
73 | GNUNET_BANDWIDTH_value_get_available_until (MIN_BANDWIDTH_PER_PEER, | ||
74 | GNUNET_TIME_UNIT_SECONDS); | ||
75 | need_per_second = need_per_peer * neighbour_count; | ||
76 | |||
77 | /* calculate inbound bandwidth per peer */ | ||
78 | distributable = 0; | ||
79 | if (bandwidth_target_in_bps > need_per_second) | ||
80 | distributable = bandwidth_target_in_bps - need_per_second; | ||
81 | share = distributable * pref_rel; | ||
82 | if (share + need_per_peer > UINT32_MAX) | ||
83 | q_in = GNUNET_BANDWIDTH_value_init (UINT32_MAX); | ||
84 | else | ||
85 | q_in = GNUNET_BANDWIDTH_value_init (need_per_peer + (uint32_t) share); | ||
86 | |||
87 | /* calculate outbound bandwidth per peer */ | ||
88 | distributable = 0; | ||
89 | if (bandwidth_target_out_bps > need_per_second) | ||
90 | distributable = bandwidth_target_out_bps - need_per_second; | ||
91 | share = distributable * pref_rel; | ||
92 | if (share + need_per_peer > UINT32_MAX) | ||
93 | q_out = GNUNET_BANDWIDTH_value_init (UINT32_MAX); | ||
94 | else | ||
95 | q_out = GNUNET_BANDWIDTH_value_init (need_per_peer + (uint32_t) share); | ||
96 | n->bw_out_internal_limit = q_out; | ||
97 | |||
98 | q_out_min = | ||
99 | GNUNET_BANDWIDTH_value_min (n->bw_out_external_limit, | ||
100 | n->bw_out_internal_limit); | ||
101 | GNUNET_BANDWIDTH_tracker_update_quota (&n->available_send_window, n->bw_out); | ||
102 | |||
103 | /* check if we want to disconnect for good due to inactivity */ | ||
104 | if ((GNUNET_TIME_absolute_get_duration (get_neighbour_timeout (n)).rel_value > | ||
105 | 0) && | ||
106 | (GNUNET_TIME_absolute_get_duration (n->time_established).rel_value > | ||
107 | GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value)) | ||
108 | { | ||
109 | #if DEBUG_CORE | ||
110 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
111 | "Forcing disconnect of `%4s' due to inactivity\n", | ||
112 | GNUNET_i2s (&n->peer)); | ||
113 | #endif | ||
114 | GNUNET_STATISTICS_update (stats, | ||
115 | gettext_noop ("# peers disconnected due to inactivity"), 1, | ||
116 | GNUNET_NO); | ||
117 | q_in = GNUNET_BANDWIDTH_value_init (0); /* force disconnect */ | ||
118 | } | ||
119 | #if DEBUG_CORE_QUOTA | ||
120 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
121 | "Current quota for `%4s' is %u/%llu b/s in (old: %u b/s) / %u out (%u internal)\n", | ||
122 | GNUNET_i2s (&n->peer), (unsigned int) ntohl (q_in.value__), | ||
123 | bandwidth_target_out_bps, (unsigned int) ntohl (n->bw_in.value__), | ||
124 | (unsigned int) ntohl (n->bw_out.value__), | ||
125 | (unsigned int) ntohl (n->bw_out_internal_limit.value__)); | ||
126 | #endif | ||
127 | if ((n->bw_in.value__ != q_in.value__) || | ||
128 | (n->bw_out.value__ != q_out_min.value__)) | ||
129 | { | ||
130 | if (n->bw_in.value__ != q_in.value__) | ||
131 | n->bw_in = q_in; | ||
132 | if (n->bw_out.value__ != q_out_min.value__) | ||
133 | n->bw_out = q_out_min; | ||
134 | if (GNUNET_YES == n->is_connected) | ||
135 | GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out); | ||
136 | handle_peer_status_change (n); | ||
137 | } | ||
138 | schedule_quota_update (n); | ||
139 | } | ||
140 | |||
141 | |||
142 | |||
143 | void | ||
144 | GSC_ATS_init () | ||
145 | { | ||
146 | if ((GNUNET_OK != | ||
147 | GNUNET_CONFIGURATION_get_value_number (c, "CORE", "TOTAL_QUOTA_IN", | ||
148 | &bandwidth_target_in_bps)) || | ||
149 | (GNUNET_OK != | ||
150 | GNUNET_CONFIGURATION_get_value_number (c, "CORE", "TOTAL_QUOTA_OUT", | ||
151 | &bandwidth_target_out_bps)) ) | ||
152 | { | ||
153 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
154 | _ | ||
155 | ("Core service is lacking key configuration settings. Exiting.\n")); | ||
156 | GNUNET_SCHEDULER_shutdown (); | ||
157 | return; | ||
158 | } | ||
159 | } | ||
diff --git a/src/core/gnunet-service-core_clients.c b/src/core/gnunet-service-core_clients.c new file mode 100644 index 000000000..ffd6d294f --- /dev/null +++ b/src/core/gnunet-service-core_clients.c | |||
@@ -0,0 +1,1102 @@ | |||
1 | |||
2 | /** | ||
3 | * Data structure for each client connected to the core service. | ||
4 | */ | ||
5 | struct Client | ||
6 | { | ||
7 | /** | ||
8 | * Clients are kept in a linked list. | ||
9 | */ | ||
10 | struct Client *next; | ||
11 | |||
12 | /** | ||
13 | * Handle for the client with the server API. | ||
14 | */ | ||
15 | struct GNUNET_SERVER_Client *client_handle; | ||
16 | |||
17 | /** | ||
18 | * Array of the types of messages this peer cares | ||
19 | * about (with "tcnt" entries). Allocated as part | ||
20 | * of this client struct, do not free! | ||
21 | */ | ||
22 | const uint16_t *types; | ||
23 | |||
24 | /** | ||
25 | * Map of peer identities to active transmission requests of this | ||
26 | * client to the peer (of type 'struct ClientActiveRequest'). | ||
27 | */ | ||
28 | struct GNUNET_CONTAINER_MultiHashMap *requests; | ||
29 | |||
30 | /** | ||
31 | * Options for messages this client cares about, | ||
32 | * see GNUNET_CORE_OPTION_ values. | ||
33 | */ | ||
34 | uint32_t options; | ||
35 | |||
36 | /** | ||
37 | * Number of types of incoming messages this client | ||
38 | * specifically cares about. Size of the "types" array. | ||
39 | */ | ||
40 | unsigned int tcnt; | ||
41 | |||
42 | }; | ||
43 | |||
44 | |||
45 | /** | ||
46 | * Record kept for each request for transmission issued by a | ||
47 | * client that is still pending. | ||
48 | */ | ||
49 | struct ClientActiveRequest | ||
50 | { | ||
51 | |||
52 | /** | ||
53 | * Active requests are kept in a doubly-linked list of | ||
54 | * the respective target peer. | ||
55 | */ | ||
56 | struct ClientActiveRequest *next; | ||
57 | |||
58 | /** | ||
59 | * Active requests are kept in a doubly-linked list of | ||
60 | * the respective target peer. | ||
61 | */ | ||
62 | struct ClientActiveRequest *prev; | ||
63 | |||
64 | /** | ||
65 | * Handle to the client. | ||
66 | */ | ||
67 | struct Client *client; | ||
68 | |||
69 | /** | ||
70 | * By what time would the client want to see this message out? | ||
71 | */ | ||
72 | struct GNUNET_TIME_Absolute deadline; | ||
73 | |||
74 | /** | ||
75 | * How important is this request. | ||
76 | */ | ||
77 | uint32_t priority; | ||
78 | |||
79 | /** | ||
80 | * How many more requests does this client have? | ||
81 | */ | ||
82 | uint32_t queue_size; | ||
83 | |||
84 | /** | ||
85 | * How many bytes does the client intend to send? | ||
86 | */ | ||
87 | uint16_t msize; | ||
88 | |||
89 | /** | ||
90 | * Unique request ID (in big endian). | ||
91 | */ | ||
92 | uint16_t smr_id; | ||
93 | |||
94 | }; | ||
95 | |||
96 | |||
97 | |||
98 | /** | ||
99 | * Linked list of our clients. | ||
100 | */ | ||
101 | static struct Client *clients; | ||
102 | |||
103 | /** | ||
104 | * Context for notifications we need to send to our clients. | ||
105 | */ | ||
106 | static struct GNUNET_SERVER_NotificationContext *notifier; | ||
107 | |||
108 | |||
109 | /** | ||
110 | * Our message stream tokenizer (for encrypted payload). | ||
111 | */ | ||
112 | static struct GNUNET_SERVER_MessageStreamTokenizer *mst; | ||
113 | |||
114 | |||
115 | |||
116 | /** | ||
117 | * Send a message to one of our clients. | ||
118 | * | ||
119 | * @param client target for the message | ||
120 | * @param msg message to transmit | ||
121 | * @param can_drop could this message be dropped if the | ||
122 | * client's queue is getting too large? | ||
123 | */ | ||
124 | static void | ||
125 | send_to_client (struct Client *client, const struct GNUNET_MessageHeader *msg, | ||
126 | int can_drop) | ||
127 | { | ||
128 | #if DEBUG_CORE_CLIENT | ||
129 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
130 | "Preparing to send %u bytes of message of type %u to client.\n", | ||
131 | (unsigned int) ntohs (msg->size), | ||
132 | (unsigned int) ntohs (msg->type)); | ||
133 | #endif | ||
134 | GNUNET_SERVER_notification_context_unicast (notifier, client->client_handle, | ||
135 | msg, can_drop); | ||
136 | } | ||
137 | |||
138 | |||
139 | |||
140 | |||
141 | |||
142 | /** | ||
143 | * Send a message to all of our current clients that have | ||
144 | * the right options set. | ||
145 | * | ||
146 | * @param msg message to multicast | ||
147 | * @param can_drop can this message be discarded if the queue is too long | ||
148 | * @param options mask to use | ||
149 | */ | ||
150 | static void | ||
151 | send_to_all_clients (const struct GNUNET_MessageHeader *msg, int can_drop, | ||
152 | int options) | ||
153 | { | ||
154 | struct Client *c; | ||
155 | |||
156 | c = clients; | ||
157 | while (c != NULL) | ||
158 | { | ||
159 | if (0 != (c->options & options)) | ||
160 | { | ||
161 | #if DEBUG_CORE_CLIENT > 1 | ||
162 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
163 | "Sending message of type %u to client.\n", | ||
164 | (unsigned int) ntohs (msg->type)); | ||
165 | #endif | ||
166 | send_to_client (c, msg, can_drop); | ||
167 | } | ||
168 | c = c->next; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | |||
173 | |||
174 | /** | ||
175 | * Handle CORE_SEND_REQUEST message. | ||
176 | */ | ||
177 | static void | ||
178 | handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client, | ||
179 | const struct GNUNET_MessageHeader *message) | ||
180 | { | ||
181 | const struct SendMessageRequest *req; | ||
182 | struct Neighbour *n; | ||
183 | struct Client *c; | ||
184 | struct ClientActiveRequest *car; | ||
185 | |||
186 | req = (const struct SendMessageRequest *) message; | ||
187 | if (0 == | ||
188 | memcmp (&req->peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))) | ||
189 | n = &self; | ||
190 | else | ||
191 | n = find_neighbour (&req->peer); | ||
192 | if ((n == NULL) || (GNUNET_YES != n->is_connected) || | ||
193 | (n->status != PEER_STATE_KEY_CONFIRMED)) | ||
194 | { | ||
195 | /* neighbour must have disconnected since request was issued, | ||
196 | * ignore (client will realize it once it processes the | ||
197 | * disconnect notification) */ | ||
198 | #if DEBUG_CORE_CLIENT | ||
199 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
200 | "Dropped client request for transmission (am disconnected)\n"); | ||
201 | #endif | ||
202 | GNUNET_STATISTICS_update (stats, | ||
203 | gettext_noop | ||
204 | ("# send requests dropped (disconnected)"), 1, | ||
205 | GNUNET_NO); | ||
206 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
207 | return; | ||
208 | } | ||
209 | c = clients; | ||
210 | while ((c != NULL) && (c->client_handle != client)) | ||
211 | c = c->next; | ||
212 | if (c == NULL) | ||
213 | { | ||
214 | /* client did not send INIT first! */ | ||
215 | GNUNET_break (0); | ||
216 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
217 | return; | ||
218 | } | ||
219 | if (c->requests == NULL) | ||
220 | c->requests = GNUNET_CONTAINER_multihashmap_create (16); | ||
221 | #if DEBUG_CORE_CLIENT | ||
222 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
223 | "Received client transmission request. queueing\n"); | ||
224 | #endif | ||
225 | car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey); | ||
226 | if (car == NULL) | ||
227 | { | ||
228 | /* create new entry */ | ||
229 | car = GNUNET_malloc (sizeof (struct ClientActiveRequest)); | ||
230 | GNUNET_assert (GNUNET_OK == | ||
231 | GNUNET_CONTAINER_multihashmap_put (c->requests, | ||
232 | &req->peer.hashPubKey, | ||
233 | car, | ||
234 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); | ||
235 | GNUNET_CONTAINER_DLL_insert (n->active_client_request_head, | ||
236 | n->active_client_request_tail, car); | ||
237 | car->client = c; | ||
238 | } | ||
239 | car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline); | ||
240 | car->priority = ntohl (req->priority); | ||
241 | car->queue_size = ntohl (req->queue_size); | ||
242 | car->msize = ntohs (req->size); | ||
243 | car->smr_id = req->smr_id; | ||
244 | schedule_peer_messages (n); | ||
245 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
246 | } | ||
247 | |||
248 | |||
249 | /** | ||
250 | * Notify client about an existing connection to one of our neighbours. | ||
251 | */ | ||
252 | static int | ||
253 | notify_client_about_neighbour (void *cls, const GNUNET_HashCode * key, | ||
254 | void *value) | ||
255 | { | ||
256 | struct Client *c = cls; | ||
257 | struct Neighbour *n = value; | ||
258 | size_t size; | ||
259 | char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; | ||
260 | struct GNUNET_TRANSPORT_ATS_Information *ats; | ||
261 | struct ConnectNotifyMessage *cnm; | ||
262 | |||
263 | size = | ||
264 | sizeof (struct ConnectNotifyMessage) + | ||
265 | (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
266 | if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) | ||
267 | { | ||
268 | GNUNET_break (0); | ||
269 | /* recovery strategy: throw away performance data */ | ||
270 | GNUNET_array_grow (n->ats, n->ats_count, 0); | ||
271 | size = | ||
272 | sizeof (struct ConnectNotifyMessage) + | ||
273 | (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
274 | } | ||
275 | cnm = (struct ConnectNotifyMessage *) buf; | ||
276 | cnm->header.size = htons (size); | ||
277 | cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); | ||
278 | cnm->ats_count = htonl (n->ats_count); | ||
279 | ats = &cnm->ats; | ||
280 | memcpy (ats, n->ats, | ||
281 | sizeof (struct GNUNET_TRANSPORT_ATS_Information) * n->ats_count); | ||
282 | ats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR); | ||
283 | ats[n->ats_count].value = htonl (0); | ||
284 | if (n->status == PEER_STATE_KEY_CONFIRMED) | ||
285 | { | ||
286 | #if DEBUG_CORE_CLIENT | ||
287 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n", | ||
288 | "NOTIFY_CONNECT"); | ||
289 | #endif | ||
290 | cnm->peer = n->peer; | ||
291 | send_to_client (c, &cnm->header, GNUNET_NO); | ||
292 | } | ||
293 | return GNUNET_OK; | ||
294 | } | ||
295 | |||
296 | |||
297 | |||
298 | /** | ||
299 | * Handle CORE_INIT request. | ||
300 | */ | ||
301 | static void | ||
302 | handle_client_init (void *cls, struct GNUNET_SERVER_Client *client, | ||
303 | const struct GNUNET_MessageHeader *message) | ||
304 | { | ||
305 | const struct InitMessage *im; | ||
306 | struct InitReplyMessage irm; | ||
307 | struct Client *c; | ||
308 | uint16_t msize; | ||
309 | const uint16_t *types; | ||
310 | uint16_t *wtypes; | ||
311 | unsigned int i; | ||
312 | |||
313 | #if DEBUG_CORE_CLIENT | ||
314 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
315 | "Client connecting to core service with `%s' message\n", "INIT"); | ||
316 | #endif | ||
317 | /* check that we don't have an entry already */ | ||
318 | c = clients; | ||
319 | while (c != NULL) | ||
320 | { | ||
321 | if (client == c->client_handle) | ||
322 | { | ||
323 | GNUNET_break (0); | ||
324 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
325 | return; | ||
326 | } | ||
327 | c = c->next; | ||
328 | } | ||
329 | msize = ntohs (message->size); | ||
330 | if (msize < sizeof (struct InitMessage)) | ||
331 | { | ||
332 | GNUNET_break (0); | ||
333 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
334 | return; | ||
335 | } | ||
336 | GNUNET_SERVER_notification_context_add (notifier, client); | ||
337 | im = (const struct InitMessage *) message; | ||
338 | types = (const uint16_t *) &im[1]; | ||
339 | msize -= sizeof (struct InitMessage); | ||
340 | c = GNUNET_malloc (sizeof (struct Client) + msize); | ||
341 | c->client_handle = client; | ||
342 | c->next = clients; | ||
343 | clients = c; | ||
344 | c->tcnt = msize / sizeof (uint16_t); | ||
345 | c->types = (const uint16_t *) &c[1]; | ||
346 | wtypes = (uint16_t *) & c[1]; | ||
347 | for (i = 0; i < c->tcnt; i++) | ||
348 | { | ||
349 | wtypes[i] = ntohs (types[i]); | ||
350 | my_type_map[wtypes[i] / 32] |= (1 << (wtypes[i] % 32)); | ||
351 | } | ||
352 | if (c->tcnt > 0) | ||
353 | broadcast_my_type_map (); | ||
354 | c->options = ntohl (im->options); | ||
355 | #if DEBUG_CORE_CLIENT | ||
356 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
357 | "Client %p is interested in %u message types\n", c, | ||
358 | (unsigned int) c->tcnt); | ||
359 | #endif | ||
360 | /* send init reply message */ | ||
361 | irm.header.size = htons (sizeof (struct InitReplyMessage)); | ||
362 | irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY); | ||
363 | irm.reserved = htonl (0); | ||
364 | memcpy (&irm.publicKey, &my_public_key, | ||
365 | sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); | ||
366 | #if DEBUG_CORE_CLIENT | ||
367 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n", | ||
368 | "INIT_REPLY"); | ||
369 | #endif | ||
370 | send_to_client (c, &irm.header, GNUNET_NO); | ||
371 | if (0 != (c->options & GNUNET_CORE_OPTION_SEND_CONNECT)) | ||
372 | { | ||
373 | /* notify new client about existing neighbours */ | ||
374 | GNUNET_CONTAINER_multihashmap_iterate (neighbours, | ||
375 | ¬ify_client_about_neighbour, c); | ||
376 | } | ||
377 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
378 | } | ||
379 | |||
380 | |||
381 | /** | ||
382 | * Free client request records. | ||
383 | * | ||
384 | * @param cls NULL | ||
385 | * @param key identity of peer for which this is an active request | ||
386 | * @param value the 'struct ClientActiveRequest' to free | ||
387 | * @return GNUNET_YES (continue iteration) | ||
388 | */ | ||
389 | static int | ||
390 | destroy_active_client_request (void *cls, const GNUNET_HashCode * key, | ||
391 | void *value) | ||
392 | { | ||
393 | struct ClientActiveRequest *car = value; | ||
394 | struct Neighbour *n; | ||
395 | struct GNUNET_PeerIdentity peer; | ||
396 | |||
397 | peer.hashPubKey = *key; | ||
398 | n = find_neighbour (&peer); | ||
399 | GNUNET_assert (NULL != n); | ||
400 | GNUNET_CONTAINER_DLL_remove (n->active_client_request_head, | ||
401 | n->active_client_request_tail, car); | ||
402 | GNUNET_free (car); | ||
403 | return GNUNET_YES; | ||
404 | } | ||
405 | |||
406 | |||
407 | /** | ||
408 | * A client disconnected, clean up. | ||
409 | * | ||
410 | * @param cls closure | ||
411 | * @param client identification of the client | ||
412 | */ | ||
413 | static void | ||
414 | handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) | ||
415 | { | ||
416 | struct Client *pos; | ||
417 | struct Client *prev; | ||
418 | unsigned int i; | ||
419 | const uint16_t *wtypes; | ||
420 | |||
421 | if (client == NULL) | ||
422 | return; | ||
423 | #if DEBUG_CORE_CLIENT | ||
424 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
425 | "Client %p has disconnected from core service.\n", client); | ||
426 | #endif | ||
427 | prev = NULL; | ||
428 | pos = clients; | ||
429 | while (pos != NULL) | ||
430 | { | ||
431 | if (client == pos->client_handle) | ||
432 | break; | ||
433 | prev = pos; | ||
434 | pos = pos->next; | ||
435 | } | ||
436 | if (pos == NULL) | ||
437 | { | ||
438 | /* client never sent INIT */ | ||
439 | return; | ||
440 | } | ||
441 | if (prev == NULL) | ||
442 | clients = pos->next; | ||
443 | else | ||
444 | prev->next = pos->next; | ||
445 | if (pos->requests != NULL) | ||
446 | { | ||
447 | GNUNET_CONTAINER_multihashmap_iterate (pos->requests, | ||
448 | &destroy_active_client_request, | ||
449 | NULL); | ||
450 | GNUNET_CONTAINER_multihashmap_destroy (pos->requests); | ||
451 | } | ||
452 | GNUNET_free (pos); | ||
453 | |||
454 | /* rebuild my_type_map */ | ||
455 | memset (my_type_map, 0, sizeof (my_type_map)); | ||
456 | for (pos = clients; NULL != pos; pos = pos->next) | ||
457 | { | ||
458 | wtypes = (const uint16_t *) &pos[1]; | ||
459 | for (i = 0; i < pos->tcnt; i++) | ||
460 | my_type_map[wtypes[i] / 32] |= (1 << (wtypes[i] % 32)); | ||
461 | } | ||
462 | broadcast_my_type_map (); | ||
463 | } | ||
464 | |||
465 | |||
466 | |||
467 | |||
468 | |||
469 | /** | ||
470 | * Handle CORE_SEND request. | ||
471 | * | ||
472 | * @param cls unused | ||
473 | * @param client the client issuing the request | ||
474 | * @param message the "struct SendMessage" | ||
475 | */ | ||
476 | static void | ||
477 | handle_client_send (void *cls, struct GNUNET_SERVER_Client *client, | ||
478 | const struct GNUNET_MessageHeader *message) | ||
479 | { | ||
480 | const struct SendMessage *sm; | ||
481 | struct Neighbour *n; | ||
482 | struct MessageEntry *prev; | ||
483 | struct MessageEntry *pos; | ||
484 | struct MessageEntry *e; | ||
485 | struct MessageEntry *min_prio_entry; | ||
486 | struct MessageEntry *min_prio_prev; | ||
487 | unsigned int min_prio; | ||
488 | unsigned int queue_size; | ||
489 | uint16_t msize; | ||
490 | |||
491 | msize = ntohs (message->size); | ||
492 | if (msize < | ||
493 | sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader)) | ||
494 | { | ||
495 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
496 | "msize is %u, should be at least %u (in %s:%d)\n", msize, | ||
497 | sizeof (struct SendMessage) + | ||
498 | sizeof (struct GNUNET_MessageHeader), __FILE__, __LINE__); | ||
499 | GNUNET_break (0); | ||
500 | if (client != NULL) | ||
501 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
502 | return; | ||
503 | } | ||
504 | sm = (const struct SendMessage *) message; | ||
505 | msize -= sizeof (struct SendMessage); | ||
506 | if (0 == | ||
507 | memcmp (&sm->peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))) | ||
508 | { | ||
509 | /* loopback */ | ||
510 | GNUNET_SERVER_mst_receive (mst, &self, (const char *) &sm[1], msize, | ||
511 | GNUNET_YES, GNUNET_NO); | ||
512 | if (client != NULL) | ||
513 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
514 | return; | ||
515 | } | ||
516 | n = find_neighbour (&sm->peer); | ||
517 | if ((n == NULL) || (GNUNET_YES != n->is_connected) || | ||
518 | (n->status != PEER_STATE_KEY_CONFIRMED)) | ||
519 | { | ||
520 | /* attempt to send message to peer that is not connected anymore | ||
521 | * (can happen due to asynchrony) */ | ||
522 | GNUNET_STATISTICS_update (stats, | ||
523 | gettext_noop | ||
524 | ("# messages discarded (disconnected)"), 1, | ||
525 | GNUNET_NO); | ||
526 | if (client != NULL) | ||
527 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
528 | return; | ||
529 | } | ||
530 | #if DEBUG_CORE | ||
531 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
532 | "Core received `%s' request, queueing %u bytes of plaintext data for transmission to `%4s'.\n", | ||
533 | "SEND", (unsigned int) msize, GNUNET_i2s (&sm->peer)); | ||
534 | #endif | ||
535 | discard_expired_messages (n); | ||
536 | /* bound queue size */ | ||
537 | /* NOTE: this entire block to bound the queue size should be | ||
538 | * obsolete with the new client-request code and the | ||
539 | * 'schedule_peer_messages' mechanism; we still have this code in | ||
540 | * here for now as a sanity check for the new mechanmism; | ||
541 | * ultimately, we should probably simply reject SEND messages that | ||
542 | * are not 'approved' (or provide a new core API for very unreliable | ||
543 | * delivery that always sends with priority 0). Food for thought. */ | ||
544 | min_prio = UINT32_MAX; | ||
545 | min_prio_entry = NULL; | ||
546 | min_prio_prev = NULL; | ||
547 | queue_size = 0; | ||
548 | prev = NULL; | ||
549 | pos = n->messages; | ||
550 | while (pos != NULL) | ||
551 | { | ||
552 | if (pos->priority <= min_prio) | ||
553 | { | ||
554 | min_prio_entry = pos; | ||
555 | min_prio_prev = prev; | ||
556 | min_prio = pos->priority; | ||
557 | } | ||
558 | queue_size++; | ||
559 | prev = pos; | ||
560 | pos = pos->next; | ||
561 | } | ||
562 | if (queue_size >= MAX_PEER_QUEUE_SIZE) | ||
563 | { | ||
564 | /* queue full */ | ||
565 | if (ntohl (sm->priority) <= min_prio) | ||
566 | { | ||
567 | /* discard new entry; this should no longer happen! */ | ||
568 | GNUNET_break (0); | ||
569 | #if DEBUG_CORE | ||
570 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
571 | "Queue full (%u/%u), discarding new request (%u bytes of type %u)\n", | ||
572 | queue_size, (unsigned int) MAX_PEER_QUEUE_SIZE, | ||
573 | (unsigned int) msize, (unsigned int) ntohs (message->type)); | ||
574 | #endif | ||
575 | GNUNET_STATISTICS_update (stats, | ||
576 | gettext_noop ("# discarded CORE_SEND requests"), | ||
577 | 1, GNUNET_NO); | ||
578 | |||
579 | if (client != NULL) | ||
580 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
581 | return; | ||
582 | } | ||
583 | GNUNET_assert (min_prio_entry != NULL); | ||
584 | /* discard "min_prio_entry" */ | ||
585 | #if DEBUG_CORE | ||
586 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
587 | "Queue full, discarding existing older request\n"); | ||
588 | #endif | ||
589 | GNUNET_STATISTICS_update (stats, | ||
590 | gettext_noop | ||
591 | ("# discarded lower priority CORE_SEND requests"), | ||
592 | 1, GNUNET_NO); | ||
593 | if (min_prio_prev == NULL) | ||
594 | n->messages = min_prio_entry->next; | ||
595 | else | ||
596 | min_prio_prev->next = min_prio_entry->next; | ||
597 | GNUNET_free (min_prio_entry); | ||
598 | } | ||
599 | |||
600 | #if DEBUG_CORE | ||
601 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
602 | "Adding transmission request for `%4s' of size %u to queue\n", | ||
603 | GNUNET_i2s (&sm->peer), (unsigned int) msize); | ||
604 | #endif | ||
605 | GNUNET_break (0 == ntohl (sm->reserved)); | ||
606 | e = GNUNET_malloc (sizeof (struct MessageEntry) + msize); | ||
607 | e->deadline = GNUNET_TIME_absolute_ntoh (sm->deadline); | ||
608 | e->priority = ntohl (sm->priority); | ||
609 | e->size = msize; | ||
610 | if (GNUNET_YES != (int) ntohl (sm->cork)) | ||
611 | e->got_slack = GNUNET_YES; | ||
612 | memcpy (&e[1], &sm[1], msize); | ||
613 | |||
614 | /* insert, keep list sorted by deadline */ | ||
615 | prev = NULL; | ||
616 | pos = n->messages; | ||
617 | while ((pos != NULL) && (pos->deadline.abs_value < e->deadline.abs_value)) | ||
618 | { | ||
619 | prev = pos; | ||
620 | pos = pos->next; | ||
621 | } | ||
622 | if (prev == NULL) | ||
623 | n->messages = e; | ||
624 | else | ||
625 | prev->next = e; | ||
626 | e->next = pos; | ||
627 | |||
628 | /* consider scheduling now */ | ||
629 | process_plaintext_neighbour_queue (n); | ||
630 | if (client != NULL) | ||
631 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
632 | } | ||
633 | |||
634 | |||
635 | /** | ||
636 | * Handle CORE_REQUEST_CONNECT request. | ||
637 | * | ||
638 | * @param cls unused | ||
639 | * @param client the client issuing the request | ||
640 | * @param message the "struct ConnectMessage" | ||
641 | */ | ||
642 | static void | ||
643 | handle_client_request_connect (void *cls, struct GNUNET_SERVER_Client *client, | ||
644 | const struct GNUNET_MessageHeader *message) | ||
645 | { | ||
646 | const struct ConnectMessage *cm = (const struct ConnectMessage *) message; | ||
647 | struct Neighbour *n; | ||
648 | |||
649 | if (0 == | ||
650 | memcmp (&cm->peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))) | ||
651 | { | ||
652 | /* In this case a client has asked us to connect to ourselves, not really an error! */ | ||
653 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
654 | return; | ||
655 | } | ||
656 | GNUNET_break (ntohl (cm->reserved) == 0); | ||
657 | #if DEBUG_CORE | ||
658 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
659 | "Core received `%s' request for `%4s', will try to establish connection\n", | ||
660 | "REQUEST_CONNECT", GNUNET_i2s (&cm->peer)); | ||
661 | #endif | ||
662 | GNUNET_STATISTICS_update (stats, | ||
663 | gettext_noop ("# connection requests received"), 1, | ||
664 | GNUNET_NO); | ||
665 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
666 | n = find_neighbour (&cm->peer); | ||
667 | if ((n == NULL) || (GNUNET_YES != n->is_connected)) | ||
668 | { | ||
669 | GNUNET_TRANSPORT_try_connect (transport, &cm->peer); | ||
670 | } | ||
671 | else | ||
672 | { | ||
673 | GNUNET_STATISTICS_update (stats, | ||
674 | gettext_noop | ||
675 | ("# connection requests ignored (already connected)"), | ||
676 | 1, GNUNET_NO); | ||
677 | } | ||
678 | } | ||
679 | |||
680 | |||
681 | |||
682 | /** | ||
683 | * Helper function for handle_client_iterate_peers. | ||
684 | * | ||
685 | * @param cls the 'struct GNUNET_SERVER_TransmitContext' to queue replies | ||
686 | * @param key identity of the connected peer | ||
687 | * @param value the 'struct Neighbour' for the peer | ||
688 | * @return GNUNET_OK (continue to iterate) | ||
689 | */ | ||
690 | static int | ||
691 | queue_connect_message (void *cls, const GNUNET_HashCode * key, void *value) | ||
692 | { | ||
693 | struct GNUNET_SERVER_TransmitContext *tc = cls; | ||
694 | struct Neighbour *n = value; | ||
695 | char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; | ||
696 | struct GNUNET_TRANSPORT_ATS_Information *ats; | ||
697 | size_t size; | ||
698 | struct ConnectNotifyMessage *cnm; | ||
699 | |||
700 | cnm = (struct ConnectNotifyMessage *) buf; | ||
701 | if (n->status != PEER_STATE_KEY_CONFIRMED) | ||
702 | return GNUNET_OK; | ||
703 | size = | ||
704 | sizeof (struct ConnectNotifyMessage) + | ||
705 | (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
706 | if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) | ||
707 | { | ||
708 | GNUNET_break (0); | ||
709 | /* recovery strategy: throw away performance data */ | ||
710 | GNUNET_array_grow (n->ats, n->ats_count, 0); | ||
711 | size = | ||
712 | sizeof (struct PeerStatusNotifyMessage) + | ||
713 | n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
714 | } | ||
715 | cnm = (struct ConnectNotifyMessage *) buf; | ||
716 | cnm->header.size = htons (size); | ||
717 | cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); | ||
718 | cnm->ats_count = htonl (n->ats_count); | ||
719 | ats = &cnm->ats; | ||
720 | memcpy (ats, n->ats, | ||
721 | n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information)); | ||
722 | ats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR); | ||
723 | ats[n->ats_count].value = htonl (0); | ||
724 | #if DEBUG_CORE_CLIENT | ||
725 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n", | ||
726 | "NOTIFY_CONNECT"); | ||
727 | #endif | ||
728 | cnm->peer = n->peer; | ||
729 | GNUNET_SERVER_transmit_context_append_message (tc, &cnm->header); | ||
730 | return GNUNET_OK; | ||
731 | } | ||
732 | |||
733 | |||
734 | /** | ||
735 | * Handle CORE_ITERATE_PEERS request. | ||
736 | * | ||
737 | * @param cls unused | ||
738 | * @param client client sending the iteration request | ||
739 | * @param message iteration request message | ||
740 | */ | ||
741 | static void | ||
742 | handle_client_iterate_peers (void *cls, struct GNUNET_SERVER_Client *client, | ||
743 | const struct GNUNET_MessageHeader *message) | ||
744 | { | ||
745 | struct GNUNET_MessageHeader done_msg; | ||
746 | struct GNUNET_SERVER_TransmitContext *tc; | ||
747 | int msize; | ||
748 | |||
749 | /* notify new client about existing neighbours */ | ||
750 | |||
751 | msize = ntohs (message->size); | ||
752 | tc = GNUNET_SERVER_transmit_context_create (client); | ||
753 | if (msize == sizeof (struct GNUNET_MessageHeader)) | ||
754 | GNUNET_CONTAINER_multihashmap_iterate (neighbours, &queue_connect_message, | ||
755 | tc); | ||
756 | else | ||
757 | GNUNET_break (0); | ||
758 | |||
759 | done_msg.size = htons (sizeof (struct GNUNET_MessageHeader)); | ||
760 | done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END); | ||
761 | GNUNET_SERVER_transmit_context_append_message (tc, &done_msg); | ||
762 | GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); | ||
763 | } | ||
764 | |||
765 | |||
766 | /** | ||
767 | * Handle CORE_PEER_CONNECTED request. Notify client about existing neighbours. | ||
768 | * | ||
769 | * @param cls unused | ||
770 | * @param client client sending the iteration request | ||
771 | * @param message iteration request message | ||
772 | */ | ||
773 | static void | ||
774 | handle_client_have_peer (void *cls, struct GNUNET_SERVER_Client *client, | ||
775 | const struct GNUNET_MessageHeader *message) | ||
776 | { | ||
777 | struct GNUNET_MessageHeader done_msg; | ||
778 | struct GNUNET_SERVER_TransmitContext *tc; | ||
779 | struct GNUNET_PeerIdentity *peer; | ||
780 | |||
781 | tc = GNUNET_SERVER_transmit_context_create (client); | ||
782 | peer = (struct GNUNET_PeerIdentity *) &message[1]; | ||
783 | GNUNET_CONTAINER_multihashmap_get_multiple (neighbours, &peer->hashPubKey, | ||
784 | &queue_connect_message, tc); | ||
785 | done_msg.size = htons (sizeof (struct GNUNET_MessageHeader)); | ||
786 | done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END); | ||
787 | GNUNET_SERVER_transmit_context_append_message (tc, &done_msg); | ||
788 | GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); | ||
789 | } | ||
790 | |||
791 | |||
792 | /** | ||
793 | * Handle REQUEST_INFO request. | ||
794 | * | ||
795 | * @param cls unused | ||
796 | * @param client client sending the request | ||
797 | * @param message iteration request message | ||
798 | */ | ||
799 | static void | ||
800 | handle_client_request_info (void *cls, struct GNUNET_SERVER_Client *client, | ||
801 | const struct GNUNET_MessageHeader *message) | ||
802 | { | ||
803 | const struct RequestInfoMessage *rcm; | ||
804 | struct Client *pos; | ||
805 | struct Neighbour *n; | ||
806 | struct ConfigurationInfoMessage cim; | ||
807 | int32_t want_reserv; | ||
808 | int32_t got_reserv; | ||
809 | unsigned long long old_preference; | ||
810 | struct GNUNET_TIME_Relative rdelay; | ||
811 | |||
812 | rdelay = GNUNET_TIME_relative_get_zero (); | ||
813 | #if DEBUG_CORE_CLIENT | ||
814 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service receives `%s' request.\n", | ||
815 | "REQUEST_INFO"); | ||
816 | #endif | ||
817 | pos = clients; | ||
818 | while (pos != NULL) | ||
819 | { | ||
820 | if (client == pos->client_handle) | ||
821 | break; | ||
822 | pos = pos->next; | ||
823 | } | ||
824 | if (pos == NULL) | ||
825 | { | ||
826 | GNUNET_break (0); | ||
827 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
828 | return; | ||
829 | } | ||
830 | |||
831 | rcm = (const struct RequestInfoMessage *) message; | ||
832 | n = find_neighbour (&rcm->peer); | ||
833 | memset (&cim, 0, sizeof (cim)); | ||
834 | if ((n != NULL) && (GNUNET_YES == n->is_connected)) | ||
835 | { | ||
836 | want_reserv = ntohl (rcm->reserve_inbound); | ||
837 | if (n->bw_out_internal_limit.value__ != rcm->limit_outbound.value__) | ||
838 | { | ||
839 | n->bw_out_internal_limit = rcm->limit_outbound; | ||
840 | if (n->bw_out.value__ != | ||
841 | GNUNET_BANDWIDTH_value_min (n->bw_out_internal_limit, | ||
842 | n->bw_out_external_limit).value__) | ||
843 | { | ||
844 | n->bw_out = | ||
845 | GNUNET_BANDWIDTH_value_min (n->bw_out_internal_limit, | ||
846 | n->bw_out_external_limit); | ||
847 | GNUNET_BANDWIDTH_tracker_update_quota (&n->available_recv_window, | ||
848 | n->bw_out); | ||
849 | GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out); | ||
850 | handle_peer_status_change (n); | ||
851 | } | ||
852 | } | ||
853 | if (want_reserv < 0) | ||
854 | { | ||
855 | got_reserv = want_reserv; | ||
856 | } | ||
857 | else if (want_reserv > 0) | ||
858 | { | ||
859 | rdelay = | ||
860 | GNUNET_BANDWIDTH_tracker_get_delay (&n->available_recv_window, | ||
861 | want_reserv); | ||
862 | if (rdelay.rel_value == 0) | ||
863 | got_reserv = want_reserv; | ||
864 | else | ||
865 | got_reserv = 0; /* all or nothing */ | ||
866 | } | ||
867 | else | ||
868 | got_reserv = 0; | ||
869 | GNUNET_BANDWIDTH_tracker_consume (&n->available_recv_window, got_reserv); | ||
870 | old_preference = n->current_preference; | ||
871 | n->current_preference += GNUNET_ntohll (rcm->preference_change); | ||
872 | if (old_preference > n->current_preference) | ||
873 | { | ||
874 | /* overflow; cap at maximum value */ | ||
875 | n->current_preference = ULLONG_MAX; | ||
876 | } | ||
877 | update_preference_sum (n->current_preference - old_preference); | ||
878 | #if DEBUG_CORE_QUOTA | ||
879 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
880 | "Received reservation request for %d bytes for peer `%4s', reserved %d bytes, suggesting delay of %llu ms\n", | ||
881 | (int) want_reserv, GNUNET_i2s (&rcm->peer), (int) got_reserv, | ||
882 | (unsigned long long) rdelay.rel_value); | ||
883 | #endif | ||
884 | cim.reserved_amount = htonl (got_reserv); | ||
885 | cim.reserve_delay = GNUNET_TIME_relative_hton (rdelay); | ||
886 | cim.bw_out = n->bw_out; | ||
887 | cim.preference = n->current_preference; | ||
888 | } | ||
889 | else | ||
890 | { | ||
891 | /* Technically, this COULD happen (due to asynchronous behavior), | ||
892 | * but it should be rare, so we should generate an info event | ||
893 | * to help diagnosis of serious errors that might be masked by this */ | ||
894 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
895 | _ | ||
896 | ("Client asked for preference change with peer `%s', which is not connected!\n"), | ||
897 | GNUNET_i2s (&rcm->peer)); | ||
898 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
899 | return; | ||
900 | } | ||
901 | cim.header.size = htons (sizeof (struct ConfigurationInfoMessage)); | ||
902 | cim.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO); | ||
903 | cim.peer = rcm->peer; | ||
904 | cim.rim_id = rcm->rim_id; | ||
905 | #if DEBUG_CORE_CLIENT | ||
906 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n", | ||
907 | "CONFIGURATION_INFO"); | ||
908 | #endif | ||
909 | send_to_client (pos, &cim.header, GNUNET_NO); | ||
910 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
911 | } | ||
912 | |||
913 | |||
914 | |||
915 | |||
916 | /** | ||
917 | * Send a P2P message to a client. | ||
918 | * | ||
919 | * @param sender who sent us the message? | ||
920 | * @param client who should we give the message to? | ||
921 | * @param m contains the message to transmit | ||
922 | * @param msize number of bytes in buf to transmit | ||
923 | */ | ||
924 | static void | ||
925 | send_p2p_message_to_client (struct Neighbour *sender, struct Client *client, | ||
926 | const void *m, size_t msize) | ||
927 | { | ||
928 | size_t size = | ||
929 | msize + sizeof (struct NotifyTrafficMessage) + | ||
930 | (sender->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
931 | char buf[size]; | ||
932 | struct NotifyTrafficMessage *ntm; | ||
933 | struct GNUNET_TRANSPORT_ATS_Information *ats; | ||
934 | |||
935 | GNUNET_assert (GNUNET_YES == sender->is_connected); | ||
936 | GNUNET_break (sender->status == PEER_STATE_KEY_CONFIRMED); | ||
937 | if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) | ||
938 | { | ||
939 | GNUNET_break (0); | ||
940 | /* recovery strategy: throw performance data away... */ | ||
941 | GNUNET_array_grow (sender->ats, sender->ats_count, 0); | ||
942 | size = | ||
943 | msize + sizeof (struct NotifyTrafficMessage) + | ||
944 | (sender->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
945 | } | ||
946 | #if DEBUG_CORE | ||
947 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
948 | "Core service passes message from `%4s' of type %u to client.\n", | ||
949 | GNUNET_i2s (&sender->peer), | ||
950 | (unsigned int) | ||
951 | ntohs (((const struct GNUNET_MessageHeader *) m)->type)); | ||
952 | #endif | ||
953 | ntm = (struct NotifyTrafficMessage *) buf; | ||
954 | ntm->header.size = htons (size); | ||
955 | ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND); | ||
956 | ntm->ats_count = htonl (sender->ats_count); | ||
957 | ntm->peer = sender->peer; | ||
958 | ats = &ntm->ats; | ||
959 | memcpy (ats, sender->ats, | ||
960 | sizeof (struct GNUNET_TRANSPORT_ATS_Information) * sender->ats_count); | ||
961 | ats[sender->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR); | ||
962 | ats[sender->ats_count].value = htonl (0); | ||
963 | memcpy (&ats[sender->ats_count + 1], m, msize); | ||
964 | send_to_client (client, &ntm->header, GNUNET_YES); | ||
965 | } | ||
966 | |||
967 | |||
968 | |||
969 | |||
970 | /** | ||
971 | * Deliver P2P message to interested clients. | ||
972 | * | ||
973 | * @param cls always NULL | ||
974 | * @param client who sent us the message (struct Neighbour) | ||
975 | * @param m the message | ||
976 | */ | ||
977 | static void | ||
978 | deliver_message (void *cls, void *client, const struct GNUNET_MessageHeader *m) | ||
979 | { | ||
980 | struct Neighbour *sender = client; | ||
981 | size_t msize = ntohs (m->size); | ||
982 | char buf[256]; | ||
983 | struct Client *cpos; | ||
984 | uint16_t type; | ||
985 | unsigned int tpos; | ||
986 | int deliver_full; | ||
987 | int dropped; | ||
988 | |||
989 | GNUNET_break (sender->status == PEER_STATE_KEY_CONFIRMED); | ||
990 | type = ntohs (m->type); | ||
991 | #if DEBUG_CORE > 1 | ||
992 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
993 | "Received encapsulated message of type %u and size %u from `%4s'\n", | ||
994 | (unsigned int) type, ntohs (m->size), GNUNET_i2s (&sender->peer)); | ||
995 | #endif | ||
996 | GNUNET_snprintf (buf, sizeof (buf), | ||
997 | gettext_noop ("# bytes of messages of type %u received"), | ||
998 | (unsigned int) type); | ||
999 | GNUNET_STATISTICS_update (stats, buf, msize, GNUNET_NO); | ||
1000 | if ((GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP == type) || | ||
1001 | (GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP == type)) | ||
1002 | { | ||
1003 | /* FIXME: update message type map for 'Neighbour' */ | ||
1004 | return; | ||
1005 | } | ||
1006 | dropped = GNUNET_YES; | ||
1007 | cpos = clients; | ||
1008 | while (cpos != NULL) | ||
1009 | { | ||
1010 | deliver_full = GNUNET_NO; | ||
1011 | if (0 != (cpos->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) | ||
1012 | deliver_full = GNUNET_YES; | ||
1013 | else | ||
1014 | { | ||
1015 | for (tpos = 0; tpos < cpos->tcnt; tpos++) | ||
1016 | { | ||
1017 | if (type != cpos->types[tpos]) | ||
1018 | continue; | ||
1019 | deliver_full = GNUNET_YES; | ||
1020 | break; | ||
1021 | } | ||
1022 | } | ||
1023 | if (GNUNET_YES == deliver_full) | ||
1024 | { | ||
1025 | send_p2p_message_to_client (sender, cpos, m, msize); | ||
1026 | dropped = GNUNET_NO; | ||
1027 | } | ||
1028 | else if (cpos->options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND) | ||
1029 | { | ||
1030 | send_p2p_message_to_client (sender, cpos, m, | ||
1031 | sizeof (struct GNUNET_MessageHeader)); | ||
1032 | } | ||
1033 | cpos = cpos->next; | ||
1034 | } | ||
1035 | if (dropped == GNUNET_YES) | ||
1036 | { | ||
1037 | #if DEBUG_CORE | ||
1038 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1039 | "Message of type %u from `%4s' not delivered to any client.\n", | ||
1040 | (unsigned int) type, GNUNET_i2s (&sender->peer)); | ||
1041 | #endif | ||
1042 | GNUNET_STATISTICS_update (stats, | ||
1043 | gettext_noop | ||
1044 | ("# messages not delivered to any client"), 1, | ||
1045 | GNUNET_NO); | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | |||
1050 | |||
1051 | void | ||
1052 | GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server) | ||
1053 | { | ||
1054 | static const struct GNUNET_SERVER_MessageHandler handlers[] = { | ||
1055 | {&handle_client_init, NULL, | ||
1056 | GNUNET_MESSAGE_TYPE_CORE_INIT, 0}, | ||
1057 | {&handle_client_iterate_peers, NULL, | ||
1058 | GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS, | ||
1059 | sizeof (struct GNUNET_MessageHeader)}, | ||
1060 | {&handle_client_have_peer, NULL, | ||
1061 | GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED, | ||
1062 | sizeof (struct GNUNET_MessageHeader) + | ||
1063 | sizeof (struct GNUNET_PeerIdentity)}, | ||
1064 | {&handle_client_request_info, NULL, | ||
1065 | GNUNET_MESSAGE_TYPE_CORE_REQUEST_INFO, | ||
1066 | sizeof (struct RequestInfoMessage)}, | ||
1067 | {&handle_client_send_request, NULL, | ||
1068 | GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST, | ||
1069 | sizeof (struct SendMessageRequest)}, | ||
1070 | {&handle_client_send, NULL, | ||
1071 | GNUNET_MESSAGE_TYPE_CORE_SEND, 0}, | ||
1072 | {&handle_client_request_connect, NULL, | ||
1073 | GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONNECT, | ||
1074 | sizeof (struct ConnectMessage)}, | ||
1075 | {NULL, NULL, 0, 0} | ||
1076 | }; | ||
1077 | |||
1078 | /* setup notification */ | ||
1079 | notifier = | ||
1080 | GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE); | ||
1081 | GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); | ||
1082 | GNUNET_SERVER_add_handlers (server, handlers); | ||
1083 | mst = GNUNET_SERVER_mst_create (&deliver_message, NULL); | ||
1084 | } | ||
1085 | |||
1086 | |||
1087 | void | ||
1088 | GSC_CLIENTS_done () | ||
1089 | { | ||
1090 | struct Client *c; | ||
1091 | |||
1092 | while (NULL != (c = clients)) | ||
1093 | handle_client_disconnect (NULL, c->client_handle); | ||
1094 | GNUNET_SERVER_notification_context_destroy (notifier); | ||
1095 | notifier = NULL; | ||
1096 | if (mst != NULL) | ||
1097 | { | ||
1098 | GNUNET_SERVER_mst_destroy (mst); | ||
1099 | mst = NULL; | ||
1100 | } | ||
1101 | |||
1102 | } | ||
diff --git a/src/core/gnunet-service-core_crypto.c b/src/core/gnunet-service-core_crypto.c new file mode 100644 index 000000000..0df6dabc1 --- /dev/null +++ b/src/core/gnunet-service-core_crypto.c | |||
@@ -0,0 +1,361 @@ | |||
1 | |||
2 | /** | ||
3 | * Our private key. | ||
4 | */ | ||
5 | static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; | ||
6 | |||
7 | /** | ||
8 | * Our public key. | ||
9 | */ | ||
10 | static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; | ||
11 | |||
12 | |||
13 | /** | ||
14 | * Derive an authentication key from "set key" information | ||
15 | */ | ||
16 | static void | ||
17 | derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey, | ||
18 | const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, | ||
19 | struct GNUNET_TIME_Absolute creation_time) | ||
20 | { | ||
21 | static const char ctx[] = "authentication key"; | ||
22 | struct GNUNET_TIME_AbsoluteNBO ctbe; | ||
23 | |||
24 | |||
25 | ctbe = GNUNET_TIME_absolute_hton (creation_time); | ||
26 | GNUNET_CRYPTO_hmac_derive_key (akey, skey, &seed, sizeof (seed), &skey->key, | ||
27 | sizeof (skey->key), &ctbe, sizeof (ctbe), ctx, | ||
28 | sizeof (ctx), NULL); | ||
29 | } | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Derive an IV from packet information | ||
34 | */ | ||
35 | static void | ||
36 | derive_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, | ||
37 | const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, | ||
38 | const struct GNUNET_PeerIdentity *identity) | ||
39 | { | ||
40 | static const char ctx[] = "initialization vector"; | ||
41 | |||
42 | GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), | ||
43 | &identity->hashPubKey.bits, | ||
44 | sizeof (identity->hashPubKey.bits), ctx, | ||
45 | sizeof (ctx), NULL); | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * Derive an IV from pong packet information | ||
50 | */ | ||
51 | static void | ||
52 | derive_pong_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, | ||
53 | const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, | ||
54 | uint32_t challenge, const struct GNUNET_PeerIdentity *identity) | ||
55 | { | ||
56 | static const char ctx[] = "pong initialization vector"; | ||
57 | |||
58 | GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), | ||
59 | &identity->hashPubKey.bits, | ||
60 | sizeof (identity->hashPubKey.bits), &challenge, | ||
61 | sizeof (challenge), ctx, sizeof (ctx), NULL); | ||
62 | } | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Encrypt size bytes from in and write the result to out. Use the | ||
67 | * key for outbound traffic of the given neighbour. | ||
68 | * | ||
69 | * @param n neighbour we are sending to | ||
70 | * @param iv initialization vector to use | ||
71 | * @param in ciphertext | ||
72 | * @param out plaintext | ||
73 | * @param size size of in/out | ||
74 | * @return GNUNET_OK on success | ||
75 | */ | ||
76 | static int | ||
77 | do_encrypt (struct Neighbour *n, | ||
78 | const struct GNUNET_CRYPTO_AesInitializationVector *iv, | ||
79 | const void *in, void *out, size_t size) | ||
80 | { | ||
81 | if (size != (uint16_t) size) | ||
82 | { | ||
83 | GNUNET_break (0); | ||
84 | return GNUNET_NO; | ||
85 | } | ||
86 | GNUNET_assert (size == | ||
87 | GNUNET_CRYPTO_aes_encrypt (in, (uint16_t) size, | ||
88 | &n->encrypt_key, iv, out)); | ||
89 | GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes encrypted"), size, | ||
90 | GNUNET_NO); | ||
91 | #if DEBUG_CORE > 2 | ||
92 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
93 | "Encrypted %u bytes for `%4s' using key %u, IV %u\n", | ||
94 | (unsigned int) size, GNUNET_i2s (&n->peer), | ||
95 | (unsigned int) n->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, | ||
96 | sizeof | ||
97 | (iv))); | ||
98 | #endif | ||
99 | return GNUNET_OK; | ||
100 | } | ||
101 | |||
102 | |||
103 | |||
104 | |||
105 | /** | ||
106 | * Decrypt size bytes from in and write the result to out. Use the | ||
107 | * key for inbound traffic of the given neighbour. This function does | ||
108 | * NOT do any integrity-checks on the result. | ||
109 | * | ||
110 | * @param n neighbour we are receiving from | ||
111 | * @param iv initialization vector to use | ||
112 | * @param in ciphertext | ||
113 | * @param out plaintext | ||
114 | * @param size size of in/out | ||
115 | * @return GNUNET_OK on success | ||
116 | */ | ||
117 | static int | ||
118 | do_decrypt (struct Neighbour *n, | ||
119 | const struct GNUNET_CRYPTO_AesInitializationVector *iv, | ||
120 | const void *in, void *out, size_t size) | ||
121 | { | ||
122 | if (size != (uint16_t) size) | ||
123 | { | ||
124 | GNUNET_break (0); | ||
125 | return GNUNET_NO; | ||
126 | } | ||
127 | if ((n->status != PEER_STATE_KEY_RECEIVED) && | ||
128 | (n->status != PEER_STATE_KEY_CONFIRMED)) | ||
129 | { | ||
130 | GNUNET_break_op (0); | ||
131 | return GNUNET_SYSERR; | ||
132 | } | ||
133 | if (size != | ||
134 | GNUNET_CRYPTO_aes_decrypt (in, (uint16_t) size, &n->decrypt_key, iv, out)) | ||
135 | { | ||
136 | GNUNET_break (0); | ||
137 | return GNUNET_SYSERR; | ||
138 | } | ||
139 | GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes decrypted"), size, | ||
140 | GNUNET_NO); | ||
141 | #if DEBUG_CORE > 1 | ||
142 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
143 | "Decrypted %u bytes from `%4s' using key %u, IV %u\n", | ||
144 | (unsigned int) size, GNUNET_i2s (&n->peer), | ||
145 | (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, | ||
146 | sizeof | ||
147 | (*iv))); | ||
148 | #endif | ||
149 | return GNUNET_OK; | ||
150 | } | ||
151 | |||
152 | |||
153 | |||
154 | /** | ||
155 | * We received an encrypted message. Decrypt, validate and | ||
156 | * pass on to the appropriate clients. | ||
157 | * | ||
158 | * @param n target of the message | ||
159 | * @param m encrypted message | ||
160 | * @param ats performance data | ||
161 | * @param ats_count number of entries in ats (excluding 0-termination) | ||
162 | */ | ||
163 | static void | ||
164 | handle_encrypted_message (struct Neighbour *n, const struct EncryptedMessage *m, | ||
165 | const struct GNUNET_TRANSPORT_ATS_Information *ats, | ||
166 | uint32_t ats_count) | ||
167 | { | ||
168 | size_t size = ntohs (m->header.size); | ||
169 | char buf[size]; | ||
170 | struct EncryptedMessage *pt; /* plaintext */ | ||
171 | GNUNET_HashCode ph; | ||
172 | uint32_t snum; | ||
173 | struct GNUNET_TIME_Absolute t; | ||
174 | struct GNUNET_CRYPTO_AesInitializationVector iv; | ||
175 | struct GNUNET_CRYPTO_AuthKey auth_key; | ||
176 | |||
177 | #if DEBUG_CORE | ||
178 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
179 | "Core service receives `%s' request from `%4s'.\n", | ||
180 | "ENCRYPTED_MESSAGE", GNUNET_i2s (&n->peer)); | ||
181 | #endif | ||
182 | /* validate hash */ | ||
183 | derive_auth_key (&auth_key, &n->decrypt_key, m->iv_seed, | ||
184 | n->decrypt_key_created); | ||
185 | GNUNET_CRYPTO_hmac (&auth_key, &m->sequence_number, | ||
186 | size - ENCRYPTED_HEADER_SIZE, &ph); | ||
187 | #if DEBUG_HANDSHAKE | ||
188 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
189 | "Re-Authenticated %u bytes of ciphertext (`%u'): `%s'\n", | ||
190 | (unsigned int) size - ENCRYPTED_HEADER_SIZE, | ||
191 | GNUNET_CRYPTO_crc32_n (&m->sequence_number, | ||
192 | size - ENCRYPTED_HEADER_SIZE), | ||
193 | GNUNET_h2s (&ph)); | ||
194 | #endif | ||
195 | |||
196 | if (0 != memcmp (&ph, &m->hmac, sizeof (GNUNET_HashCode))) | ||
197 | { | ||
198 | /* checksum failed */ | ||
199 | GNUNET_break_op (0); | ||
200 | return; | ||
201 | } | ||
202 | derive_iv (&iv, &n->decrypt_key, m->iv_seed, &my_identity); | ||
203 | /* decrypt */ | ||
204 | if (GNUNET_OK != | ||
205 | do_decrypt (n, &iv, &m->sequence_number, &buf[ENCRYPTED_HEADER_SIZE], | ||
206 | size - ENCRYPTED_HEADER_SIZE)) | ||
207 | return; | ||
208 | pt = (struct EncryptedMessage *) buf; | ||
209 | |||
210 | /* validate sequence number */ | ||
211 | snum = ntohl (pt->sequence_number); | ||
212 | if (n->last_sequence_number_received == snum) | ||
213 | { | ||
214 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
215 | "Received duplicate message, ignoring.\n"); | ||
216 | /* duplicate, ignore */ | ||
217 | GNUNET_STATISTICS_update (stats, | ||
218 | gettext_noop ("# bytes dropped (duplicates)"), | ||
219 | size, GNUNET_NO); | ||
220 | return; | ||
221 | } | ||
222 | if ((n->last_sequence_number_received > snum) && | ||
223 | (n->last_sequence_number_received - snum > 32)) | ||
224 | { | ||
225 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
226 | "Received ancient out of sequence message, ignoring.\n"); | ||
227 | /* ancient out of sequence, ignore */ | ||
228 | GNUNET_STATISTICS_update (stats, | ||
229 | gettext_noop | ||
230 | ("# bytes dropped (out of sequence)"), size, | ||
231 | GNUNET_NO); | ||
232 | return; | ||
233 | } | ||
234 | if (n->last_sequence_number_received > snum) | ||
235 | { | ||
236 | unsigned int rotbit = 1 << (n->last_sequence_number_received - snum - 1); | ||
237 | |||
238 | if ((n->last_packets_bitmap & rotbit) != 0) | ||
239 | { | ||
240 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
241 | "Received duplicate message, ignoring.\n"); | ||
242 | GNUNET_STATISTICS_update (stats, | ||
243 | gettext_noop ("# bytes dropped (duplicates)"), | ||
244 | size, GNUNET_NO); | ||
245 | /* duplicate, ignore */ | ||
246 | return; | ||
247 | } | ||
248 | n->last_packets_bitmap |= rotbit; | ||
249 | } | ||
250 | if (n->last_sequence_number_received < snum) | ||
251 | { | ||
252 | int shift = (snum - n->last_sequence_number_received); | ||
253 | |||
254 | if (shift >= 8 * sizeof (n->last_packets_bitmap)) | ||
255 | n->last_packets_bitmap = 0; | ||
256 | else | ||
257 | n->last_packets_bitmap <<= shift; | ||
258 | n->last_sequence_number_received = snum; | ||
259 | } | ||
260 | |||
261 | /* check timestamp */ | ||
262 | t = GNUNET_TIME_absolute_ntoh (pt->timestamp); | ||
263 | if (GNUNET_TIME_absolute_get_duration (t).rel_value > | ||
264 | MAX_MESSAGE_AGE.rel_value) | ||
265 | { | ||
266 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
267 | _("Message received far too old (%llu ms). Content ignored.\n"), | ||
268 | GNUNET_TIME_absolute_get_duration (t).rel_value); | ||
269 | GNUNET_STATISTICS_update (stats, | ||
270 | gettext_noop | ||
271 | ("# bytes dropped (ancient message)"), size, | ||
272 | GNUNET_NO); | ||
273 | return; | ||
274 | } | ||
275 | |||
276 | /* process decrypted message(s) */ | ||
277 | if (n->bw_out_external_limit.value__ != pt->inbound_bw_limit.value__) | ||
278 | { | ||
279 | #if DEBUG_CORE_SET_QUOTA | ||
280 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
281 | "Received %u b/s as new inbound limit for peer `%4s'\n", | ||
282 | (unsigned int) ntohl (pt->inbound_bw_limit.value__), | ||
283 | GNUNET_i2s (&n->peer)); | ||
284 | #endif | ||
285 | n->bw_out_external_limit = pt->inbound_bw_limit; | ||
286 | n->bw_out = | ||
287 | GNUNET_BANDWIDTH_value_min (n->bw_out_external_limit, | ||
288 | n->bw_out_internal_limit); | ||
289 | GNUNET_BANDWIDTH_tracker_update_quota (&n->available_send_window, | ||
290 | n->bw_out); | ||
291 | GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out); | ||
292 | } | ||
293 | n->last_activity = GNUNET_TIME_absolute_get (); | ||
294 | if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) | ||
295 | GNUNET_SCHEDULER_cancel (n->keep_alive_task); | ||
296 | n->keep_alive_task = | ||
297 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide | ||
298 | (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, | ||
299 | 2), &send_keep_alive, n); | ||
300 | GNUNET_STATISTICS_update (stats, | ||
301 | gettext_noop ("# bytes of payload decrypted"), | ||
302 | size - sizeof (struct EncryptedMessage), GNUNET_NO); | ||
303 | handle_peer_status_change (n); | ||
304 | update_neighbour_performance (n, ats, ats_count); | ||
305 | if (GNUNET_OK != | ||
306 | GNUNET_SERVER_mst_receive (mst, n, &buf[sizeof (struct EncryptedMessage)], | ||
307 | size - sizeof (struct EncryptedMessage), | ||
308 | GNUNET_YES, GNUNET_NO)) | ||
309 | GNUNET_break_op (0); | ||
310 | } | ||
311 | |||
312 | |||
313 | /** | ||
314 | * Wrapper around 'free_neighbour'; helper for 'cleaning_task'. | ||
315 | */ | ||
316 | static int | ||
317 | free_neighbour_helper (void *cls, const GNUNET_HashCode * key, void *value) | ||
318 | { | ||
319 | struct Neighbour *n = value; | ||
320 | |||
321 | free_neighbour (n); | ||
322 | return GNUNET_OK; | ||
323 | } | ||
324 | |||
325 | |||
326 | int | ||
327 | GSC_CRYPTO_init () | ||
328 | { | ||
329 | char *keyfile; | ||
330 | |||
331 | if (GNUNET_OK != | ||
332 | GNUNET_CONFIGURATION_get_value_filename (GSC_cfg, "GNUNETD", "HOSTKEY", | ||
333 | &keyfile)) | ||
334 | { | ||
335 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
336 | _ | ||
337 | ("Core service is lacking HOSTKEY configuration setting. Exiting.\n")); | ||
338 | return GNUNET_SYSERR; | ||
339 | } | ||
340 | my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); | ||
341 | GNUNET_free (keyfile); | ||
342 | if (my_private_key == NULL) | ||
343 | { | ||
344 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
345 | _("Core service could not access hostkey. Exiting.\n")); | ||
346 | return GNUNET_SYSERR; | ||
347 | } | ||
348 | GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); | ||
349 | GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), | ||
350 | &my_identity.hashPubKey); | ||
351 | |||
352 | return GNUNET_OK; | ||
353 | } | ||
354 | |||
355 | |||
356 | void | ||
357 | GSC_CRYPTO_done () | ||
358 | { | ||
359 | if (my_private_key != NULL) | ||
360 | GNUNET_CRYPTO_rsa_key_free (my_private_key); | ||
361 | } | ||
diff --git a/src/core/gnunet-service-core_extern.c b/src/core/gnunet-service-core_extern.c new file mode 100644 index 000000000..0d2a3172b --- /dev/null +++ b/src/core/gnunet-service-core_extern.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* code that should be moved outside of core/ entirely */ | ||
2 | |||
3 | /** | ||
4 | * Merge the given performance data with the data we currently | ||
5 | * track for the given neighbour. | ||
6 | * | ||
7 | * @param n neighbour | ||
8 | * @param ats new performance data | ||
9 | * @param ats_count number of records in ats | ||
10 | */ | ||
11 | static void | ||
12 | update_neighbour_performance (struct Neighbour *n, | ||
13 | const struct GNUNET_TRANSPORT_ATS_Information | ||
14 | *ats, uint32_t ats_count) | ||
15 | { | ||
16 | uint32_t i; | ||
17 | unsigned int j; | ||
18 | |||
19 | if (ats_count == 0) | ||
20 | return; | ||
21 | for (i = 0; i < ats_count; i++) | ||
22 | { | ||
23 | for (j = 0; j < n->ats_count; j++) | ||
24 | { | ||
25 | if (n->ats[j].type == ats[i].type) | ||
26 | { | ||
27 | n->ats[j].value = ats[i].value; | ||
28 | break; | ||
29 | } | ||
30 | } | ||
31 | if (j == n->ats_count) | ||
32 | { | ||
33 | GNUNET_array_append (n->ats, n->ats_count, ats[i]); | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | |||
39 | |||
diff --git a/src/core/gnunet-service-core_kx.c b/src/core/gnunet-service-core_kx.c new file mode 100644 index 000000000..ac5b08d79 --- /dev/null +++ b/src/core/gnunet-service-core_kx.c | |||
@@ -0,0 +1,958 @@ | |||
1 | |||
2 | /** | ||
3 | * We're sending an (encrypted) PING to the other peer to check if he | ||
4 | * can decrypt. The other peer should respond with a PONG with the | ||
5 | * same content, except this time encrypted with the receiver's key. | ||
6 | */ | ||
7 | struct PingMessage | ||
8 | { | ||
9 | /** | ||
10 | * Message type is CORE_PING. | ||
11 | */ | ||
12 | struct GNUNET_MessageHeader header; | ||
13 | |||
14 | /** | ||
15 | * Seed for the IV | ||
16 | */ | ||
17 | uint32_t iv_seed GNUNET_PACKED; | ||
18 | |||
19 | /** | ||
20 | * Intended target of the PING, used primarily to check | ||
21 | * that decryption actually worked. | ||
22 | */ | ||
23 | struct GNUNET_PeerIdentity target; | ||
24 | |||
25 | /** | ||
26 | * Random number chosen to make reply harder. | ||
27 | */ | ||
28 | uint32_t challenge GNUNET_PACKED; | ||
29 | }; | ||
30 | |||
31 | |||
32 | |||
33 | /** | ||
34 | * Response to a PING. Includes data from the original PING | ||
35 | * plus initial bandwidth quota information. | ||
36 | */ | ||
37 | struct PongMessage | ||
38 | { | ||
39 | /** | ||
40 | * Message type is CORE_PONG. | ||
41 | */ | ||
42 | struct GNUNET_MessageHeader header; | ||
43 | |||
44 | /** | ||
45 | * Seed for the IV | ||
46 | */ | ||
47 | uint32_t iv_seed GNUNET_PACKED; | ||
48 | |||
49 | /** | ||
50 | * Random number to make faking the reply harder. Must be | ||
51 | * first field after header (this is where we start to encrypt!). | ||
52 | */ | ||
53 | uint32_t challenge GNUNET_PACKED; | ||
54 | |||
55 | /** | ||
56 | * Desired bandwidth (how much we should send to this | ||
57 | * peer / how much is the sender willing to receive). | ||
58 | */ | ||
59 | struct GNUNET_BANDWIDTH_Value32NBO inbound_bw_limit; | ||
60 | |||
61 | /** | ||
62 | * Intended target of the PING, used primarily to check | ||
63 | * that decryption actually worked. | ||
64 | */ | ||
65 | struct GNUNET_PeerIdentity target; | ||
66 | }; | ||
67 | |||
68 | |||
69 | /** | ||
70 | * Message transmitted to set (or update) a session key. | ||
71 | */ | ||
72 | struct SetKeyMessage | ||
73 | { | ||
74 | |||
75 | /** | ||
76 | * Message type is either CORE_SET_KEY. | ||
77 | */ | ||
78 | struct GNUNET_MessageHeader header; | ||
79 | |||
80 | /** | ||
81 | * Status of the sender (should be in "enum PeerStateMachine"), nbo. | ||
82 | */ | ||
83 | int32_t sender_status GNUNET_PACKED; | ||
84 | |||
85 | /** | ||
86 | * Purpose of the signature, will be | ||
87 | * GNUNET_SIGNATURE_PURPOSE_SET_KEY. | ||
88 | */ | ||
89 | struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; | ||
90 | |||
91 | /** | ||
92 | * At what time was this key created? | ||
93 | */ | ||
94 | struct GNUNET_TIME_AbsoluteNBO creation_time; | ||
95 | |||
96 | /** | ||
97 | * The encrypted session key. | ||
98 | */ | ||
99 | struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key; | ||
100 | |||
101 | /** | ||
102 | * Who is the intended recipient? | ||
103 | */ | ||
104 | struct GNUNET_PeerIdentity target; | ||
105 | |||
106 | /** | ||
107 | * Signature of the stuff above (starting at purpose). | ||
108 | */ | ||
109 | struct GNUNET_CRYPTO_RsaSignature signature; | ||
110 | |||
111 | }; | ||
112 | |||
113 | |||
114 | /** | ||
115 | * Handle to peerinfo service. | ||
116 | */ | ||
117 | static struct GNUNET_PEERINFO_Handle *peerinfo; | ||
118 | |||
119 | |||
120 | |||
121 | /** | ||
122 | * We received a PING message. Validate and transmit | ||
123 | * PONG. | ||
124 | * | ||
125 | * @param n sender of the PING | ||
126 | * @param m the encrypted PING message itself | ||
127 | * @param ats performance data | ||
128 | * @param ats_count number of entries in ats (excluding 0-termination) | ||
129 | */ | ||
130 | static void | ||
131 | handle_ping (struct Neighbour *n, const struct PingMessage *m, | ||
132 | const struct GNUNET_TRANSPORT_ATS_Information *ats, | ||
133 | uint32_t ats_count) | ||
134 | { | ||
135 | struct PingMessage t; | ||
136 | struct PongMessage tx; | ||
137 | struct PongMessage *tp; | ||
138 | struct MessageEntry *me; | ||
139 | struct GNUNET_CRYPTO_AesInitializationVector iv; | ||
140 | |||
141 | #if DEBUG_CORE | ||
142 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
143 | "Core service receives `%s' request from `%4s'.\n", "PING", | ||
144 | GNUNET_i2s (&n->peer)); | ||
145 | #endif | ||
146 | derive_iv (&iv, &n->decrypt_key, m->iv_seed, &my_identity); | ||
147 | if (GNUNET_OK != | ||
148 | do_decrypt (n, &iv, &m->target, &t.target, | ||
149 | sizeof (struct PingMessage) - ((void *) &m->target - | ||
150 | (void *) m))) | ||
151 | return; | ||
152 | #if DEBUG_HANDSHAKE | ||
153 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
154 | "Decrypted `%s' to `%4s' with challenge %u decrypted using key %u, IV %u (salt %u)\n", | ||
155 | "PING", GNUNET_i2s (&t.target), (unsigned int) t.challenge, | ||
156 | (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, | ||
157 | sizeof | ||
158 | (iv)), | ||
159 | m->iv_seed); | ||
160 | #endif | ||
161 | GNUNET_STATISTICS_update (stats, gettext_noop ("# PING messages decrypted"), | ||
162 | 1, GNUNET_NO); | ||
163 | if (0 != | ||
164 | memcmp (&t.target, &my_identity, sizeof (struct GNUNET_PeerIdentity))) | ||
165 | { | ||
166 | char sender[9]; | ||
167 | char peer[9]; | ||
168 | |||
169 | GNUNET_snprintf (sender, sizeof (sender), "%8s", GNUNET_i2s (&n->peer)); | ||
170 | GNUNET_snprintf (peer, sizeof (peer), "%8s", GNUNET_i2s (&t.target)); | ||
171 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
172 | _ | ||
173 | ("Received PING from `%s' for different identity: I am `%s', PONG identity: `%s'\n"), | ||
174 | sender, GNUNET_i2s (&my_identity), peer); | ||
175 | GNUNET_break_op (0); | ||
176 | return; | ||
177 | } | ||
178 | update_neighbour_performance (n, ats, ats_count); | ||
179 | me = GNUNET_malloc (sizeof (struct MessageEntry) + | ||
180 | sizeof (struct PongMessage)); | ||
181 | GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head, n->encrypted_tail, | ||
182 | n->encrypted_tail, me); | ||
183 | me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PONG_DELAY); | ||
184 | me->priority = PONG_PRIORITY; | ||
185 | me->size = sizeof (struct PongMessage); | ||
186 | tx.inbound_bw_limit = n->bw_in; | ||
187 | tx.challenge = t.challenge; | ||
188 | tx.target = t.target; | ||
189 | tp = (struct PongMessage *) &me[1]; | ||
190 | tp->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG); | ||
191 | tp->header.size = htons (sizeof (struct PongMessage)); | ||
192 | tp->iv_seed = | ||
193 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); | ||
194 | derive_pong_iv (&iv, &n->encrypt_key, tp->iv_seed, t.challenge, &n->peer); | ||
195 | do_encrypt (n, &iv, &tx.challenge, &tp->challenge, | ||
196 | sizeof (struct PongMessage) - ((void *) &tp->challenge - | ||
197 | (void *) tp)); | ||
198 | GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages created"), 1, | ||
199 | GNUNET_NO); | ||
200 | #if DEBUG_HANDSHAKE | ||
201 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
202 | "Encrypting `%s' with challenge %u using key %u, IV %u (salt %u)\n", | ||
203 | "PONG", (unsigned int) t.challenge, | ||
204 | (unsigned int) n->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, | ||
205 | sizeof | ||
206 | (iv)), | ||
207 | tp->iv_seed); | ||
208 | #endif | ||
209 | /* trigger queue processing */ | ||
210 | process_encrypted_neighbour_queue (n); | ||
211 | } | ||
212 | |||
213 | |||
214 | /** | ||
215 | * We received a PONG message. Validate and update our status. | ||
216 | * | ||
217 | * @param n sender of the PONG | ||
218 | * @param m the encrypted PONG message itself | ||
219 | * @param ats performance data | ||
220 | * @param ats_count number of entries in ats (excluding 0-termination) | ||
221 | */ | ||
222 | static void | ||
223 | handle_pong (struct Neighbour *n, const struct PongMessage *m, | ||
224 | const struct GNUNET_TRANSPORT_ATS_Information *ats, | ||
225 | uint32_t ats_count) | ||
226 | { | ||
227 | struct PongMessage t; | ||
228 | struct ConnectNotifyMessage *cnm; | ||
229 | struct GNUNET_CRYPTO_AesInitializationVector iv; | ||
230 | char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; | ||
231 | struct GNUNET_TRANSPORT_ATS_Information *mats; | ||
232 | size_t size; | ||
233 | |||
234 | #if DEBUG_HANDSHAKE | ||
235 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
236 | "Core service receives `%s' response from `%4s'.\n", "PONG", | ||
237 | GNUNET_i2s (&n->peer)); | ||
238 | #endif | ||
239 | /* mark as garbage, just to be sure */ | ||
240 | memset (&t, 255, sizeof (t)); | ||
241 | derive_pong_iv (&iv, &n->decrypt_key, m->iv_seed, n->ping_challenge, | ||
242 | &my_identity); | ||
243 | if (GNUNET_OK != | ||
244 | do_decrypt (n, &iv, &m->challenge, &t.challenge, | ||
245 | sizeof (struct PongMessage) - ((void *) &m->challenge - | ||
246 | (void *) m))) | ||
247 | { | ||
248 | GNUNET_break_op (0); | ||
249 | return; | ||
250 | } | ||
251 | GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages decrypted"), | ||
252 | 1, GNUNET_NO); | ||
253 | #if DEBUG_HANDSHAKE | ||
254 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
255 | "Decrypted `%s' from `%4s' with challenge %u using key %u, IV %u (salt %u)\n", | ||
256 | "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge, | ||
257 | (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, | ||
258 | sizeof | ||
259 | (iv)), | ||
260 | m->iv_seed); | ||
261 | #endif | ||
262 | if ((0 != memcmp (&t.target, &n->peer, sizeof (struct GNUNET_PeerIdentity))) | ||
263 | || (n->ping_challenge != t.challenge)) | ||
264 | { | ||
265 | /* PONG malformed */ | ||
266 | #if DEBUG_CORE | ||
267 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
268 | "Received malformed `%s' wanted sender `%4s' with challenge %u\n", | ||
269 | "PONG", GNUNET_i2s (&n->peer), | ||
270 | (unsigned int) n->ping_challenge); | ||
271 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
272 | "Received malformed `%s' received from `%4s' with challenge %u\n", | ||
273 | "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge); | ||
274 | #endif | ||
275 | GNUNET_break_op (n->ping_challenge != t.challenge); | ||
276 | return; | ||
277 | } | ||
278 | switch (n->status) | ||
279 | { | ||
280 | case PEER_STATE_DOWN: | ||
281 | GNUNET_break (0); /* should be impossible */ | ||
282 | return; | ||
283 | case PEER_STATE_KEY_SENT: | ||
284 | GNUNET_break (0); /* should be impossible, how did we decrypt? */ | ||
285 | return; | ||
286 | case PEER_STATE_KEY_RECEIVED: | ||
287 | GNUNET_STATISTICS_update (stats, | ||
288 | gettext_noop | ||
289 | ("# Session keys confirmed via PONG"), 1, | ||
290 | GNUNET_NO); | ||
291 | n->status = PEER_STATE_KEY_CONFIRMED; | ||
292 | { | ||
293 | struct GNUNET_MessageHeader *hdr; | ||
294 | |||
295 | hdr = compute_type_map_message (); | ||
296 | send_type_map_to_neighbour (hdr, &n->peer.hashPubKey, n); | ||
297 | GNUNET_free (hdr); | ||
298 | } | ||
299 | if (n->bw_out_external_limit.value__ != t.inbound_bw_limit.value__) | ||
300 | { | ||
301 | n->bw_out_external_limit = t.inbound_bw_limit; | ||
302 | n->bw_out = | ||
303 | GNUNET_BANDWIDTH_value_min (n->bw_out_external_limit, | ||
304 | n->bw_out_internal_limit); | ||
305 | GNUNET_BANDWIDTH_tracker_update_quota (&n->available_send_window, | ||
306 | n->bw_out); | ||
307 | GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out); | ||
308 | } | ||
309 | #if DEBUG_CORE | ||
310 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
311 | "Confirmed key via `%s' message for peer `%4s'\n", "PONG", | ||
312 | GNUNET_i2s (&n->peer)); | ||
313 | #endif | ||
314 | if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) | ||
315 | { | ||
316 | GNUNET_SCHEDULER_cancel (n->retry_set_key_task); | ||
317 | n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; | ||
318 | } | ||
319 | update_neighbour_performance (n, ats, ats_count); | ||
320 | size = | ||
321 | sizeof (struct ConnectNotifyMessage) + | ||
322 | (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
323 | if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) | ||
324 | { | ||
325 | GNUNET_break (0); | ||
326 | /* recovery strategy: throw away performance data */ | ||
327 | GNUNET_array_grow (n->ats, n->ats_count, 0); | ||
328 | size = | ||
329 | sizeof (struct PeerStatusNotifyMessage) + | ||
330 | n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
331 | } | ||
332 | cnm = (struct ConnectNotifyMessage *) buf; | ||
333 | cnm->header.size = htons (size); | ||
334 | cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); | ||
335 | cnm->ats_count = htonl (n->ats_count); | ||
336 | cnm->peer = n->peer; | ||
337 | mats = &cnm->ats; | ||
338 | memcpy (mats, n->ats, | ||
339 | n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information)); | ||
340 | mats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR); | ||
341 | mats[n->ats_count].value = htonl (0); | ||
342 | send_to_all_clients (&cnm->header, GNUNET_NO, | ||
343 | GNUNET_CORE_OPTION_SEND_CONNECT); | ||
344 | process_encrypted_neighbour_queue (n); | ||
345 | /* fall-through! */ | ||
346 | case PEER_STATE_KEY_CONFIRMED: | ||
347 | n->last_activity = GNUNET_TIME_absolute_get (); | ||
348 | if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) | ||
349 | GNUNET_SCHEDULER_cancel (n->keep_alive_task); | ||
350 | n->keep_alive_task = | ||
351 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide | ||
352 | (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, | ||
353 | 2), &send_keep_alive, n); | ||
354 | handle_peer_status_change (n); | ||
355 | break; | ||
356 | default: | ||
357 | GNUNET_break (0); | ||
358 | break; | ||
359 | } | ||
360 | } | ||
361 | |||
362 | |||
363 | /** | ||
364 | * We received a SET_KEY message. Validate and update | ||
365 | * our key material and status. | ||
366 | * | ||
367 | * @param n the neighbour from which we received message m | ||
368 | * @param m the set key message we received | ||
369 | * @param ats performance data | ||
370 | * @param ats_count number of entries in ats (excluding 0-termination) | ||
371 | */ | ||
372 | static void | ||
373 | handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m, | ||
374 | const struct GNUNET_TRANSPORT_ATS_Information *ats, | ||
375 | uint32_t ats_count) | ||
376 | { | ||
377 | struct SetKeyMessage *m_cpy; | ||
378 | struct GNUNET_TIME_Absolute t; | ||
379 | struct GNUNET_CRYPTO_AesSessionKey k; | ||
380 | struct PingMessage *ping; | ||
381 | struct PongMessage *pong; | ||
382 | enum PeerStateMachine sender_status; | ||
383 | |||
384 | #if DEBUG_CORE | ||
385 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
386 | "Core service receives `%s' request from `%4s'.\n", "SET_KEY", | ||
387 | GNUNET_i2s (&n->peer)); | ||
388 | #endif | ||
389 | if (n->public_key == NULL) | ||
390 | { | ||
391 | if (n->pitr != NULL) | ||
392 | { | ||
393 | #if DEBUG_CORE | ||
394 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
395 | "Ignoring `%s' message due to lack of public key for peer (still trying to obtain one).\n", | ||
396 | "SET_KEY"); | ||
397 | #endif | ||
398 | return; | ||
399 | } | ||
400 | #if DEBUG_CORE | ||
401 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
402 | "Lacking public key for peer, trying to obtain one (handle_set_key).\n"); | ||
403 | #endif | ||
404 | m_cpy = GNUNET_malloc (sizeof (struct SetKeyMessage)); | ||
405 | memcpy (m_cpy, m, sizeof (struct SetKeyMessage)); | ||
406 | /* lookup n's public key, then try again */ | ||
407 | GNUNET_assert (n->skm == NULL); | ||
408 | n->skm = m_cpy; | ||
409 | n->pitr = | ||
410 | GNUNET_PEERINFO_iterate (peerinfo, &n->peer, GNUNET_TIME_UNIT_MINUTES, | ||
411 | &process_hello_retry_handle_set_key, n); | ||
412 | GNUNET_STATISTICS_update (stats, | ||
413 | gettext_noop | ||
414 | ("# SET_KEY messages deferred (need public key)"), | ||
415 | 1, GNUNET_NO); | ||
416 | return; | ||
417 | } | ||
418 | if (0 != | ||
419 | memcmp (&m->target, &my_identity, sizeof (struct GNUNET_PeerIdentity))) | ||
420 | { | ||
421 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
422 | _ | ||
423 | ("Received `%s' message that was for `%s', not for me. Ignoring.\n"), | ||
424 | "SET_KEY", GNUNET_i2s (&m->target)); | ||
425 | return; | ||
426 | } | ||
427 | if ((ntohl (m->purpose.size) != | ||
428 | sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + | ||
429 | sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
430 | sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) + | ||
431 | sizeof (struct GNUNET_PeerIdentity)) || | ||
432 | (GNUNET_OK != | ||
433 | GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_SET_KEY, &m->purpose, | ||
434 | &m->signature, n->public_key))) | ||
435 | { | ||
436 | /* invalid signature */ | ||
437 | GNUNET_break_op (0); | ||
438 | return; | ||
439 | } | ||
440 | t = GNUNET_TIME_absolute_ntoh (m->creation_time); | ||
441 | if (((n->status == PEER_STATE_KEY_RECEIVED) || | ||
442 | (n->status == PEER_STATE_KEY_CONFIRMED)) && | ||
443 | (t.abs_value < n->decrypt_key_created.abs_value)) | ||
444 | { | ||
445 | /* this could rarely happen due to massive re-ordering of | ||
446 | * messages on the network level, but is most likely either | ||
447 | * a bug or some adversary messing with us. Report. */ | ||
448 | GNUNET_break_op (0); | ||
449 | return; | ||
450 | } | ||
451 | #if DEBUG_CORE | ||
452 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decrypting key material.\n"); | ||
453 | #endif | ||
454 | if ((GNUNET_CRYPTO_rsa_decrypt | ||
455 | (my_private_key, &m->encrypted_key, &k, | ||
456 | sizeof (struct GNUNET_CRYPTO_AesSessionKey)) != | ||
457 | sizeof (struct GNUNET_CRYPTO_AesSessionKey)) || | ||
458 | (GNUNET_OK != GNUNET_CRYPTO_aes_check_session_key (&k))) | ||
459 | { | ||
460 | /* failed to decrypt !? */ | ||
461 | GNUNET_break_op (0); | ||
462 | return; | ||
463 | } | ||
464 | GNUNET_STATISTICS_update (stats, | ||
465 | gettext_noop ("# SET_KEY messages decrypted"), 1, | ||
466 | GNUNET_NO); | ||
467 | n->decrypt_key = k; | ||
468 | if (n->decrypt_key_created.abs_value != t.abs_value) | ||
469 | { | ||
470 | /* fresh key, reset sequence numbers */ | ||
471 | n->last_sequence_number_received = 0; | ||
472 | n->last_packets_bitmap = 0; | ||
473 | n->decrypt_key_created = t; | ||
474 | } | ||
475 | update_neighbour_performance (n, ats, ats_count); | ||
476 | sender_status = (enum PeerStateMachine) ntohl (m->sender_status); | ||
477 | switch (n->status) | ||
478 | { | ||
479 | case PEER_STATE_DOWN: | ||
480 | n->status = PEER_STATE_KEY_RECEIVED; | ||
481 | #if DEBUG_CORE | ||
482 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
483 | "Responding to `%s' with my own key.\n", "SET_KEY"); | ||
484 | #endif | ||
485 | send_key (n); | ||
486 | break; | ||
487 | case PEER_STATE_KEY_SENT: | ||
488 | case PEER_STATE_KEY_RECEIVED: | ||
489 | n->status = PEER_STATE_KEY_RECEIVED; | ||
490 | if ((sender_status != PEER_STATE_KEY_RECEIVED) && | ||
491 | (sender_status != PEER_STATE_KEY_CONFIRMED)) | ||
492 | { | ||
493 | #if DEBUG_CORE | ||
494 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
495 | "Responding to `%s' with my own key (other peer has status %u).\n", | ||
496 | "SET_KEY", (unsigned int) sender_status); | ||
497 | #endif | ||
498 | send_key (n); | ||
499 | } | ||
500 | break; | ||
501 | case PEER_STATE_KEY_CONFIRMED: | ||
502 | if ((sender_status != PEER_STATE_KEY_RECEIVED) && | ||
503 | (sender_status != PEER_STATE_KEY_CONFIRMED)) | ||
504 | { | ||
505 | #if DEBUG_CORE | ||
506 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
507 | "Responding to `%s' with my own key (other peer has status %u), I was already fully up.\n", | ||
508 | "SET_KEY", (unsigned int) sender_status); | ||
509 | #endif | ||
510 | send_key (n); | ||
511 | } | ||
512 | break; | ||
513 | default: | ||
514 | GNUNET_break (0); | ||
515 | break; | ||
516 | } | ||
517 | if (n->pending_ping != NULL) | ||
518 | { | ||
519 | ping = n->pending_ping; | ||
520 | n->pending_ping = NULL; | ||
521 | handle_ping (n, ping, NULL, 0); | ||
522 | GNUNET_free (ping); | ||
523 | } | ||
524 | if (n->pending_pong != NULL) | ||
525 | { | ||
526 | pong = n->pending_pong; | ||
527 | n->pending_pong = NULL; | ||
528 | handle_pong (n, pong, NULL, 0); | ||
529 | GNUNET_free (pong); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | |||
534 | |||
535 | /** | ||
536 | * PEERINFO is giving us a HELLO for a peer. Add the public key to | ||
537 | * the neighbour's struct and retry send_key. Or, if we did not get a | ||
538 | * HELLO, just do nothing. | ||
539 | * | ||
540 | * @param cls the 'struct Neighbour' to retry sending the key for | ||
541 | * @param peer the peer for which this is the HELLO | ||
542 | * @param hello HELLO message of that peer | ||
543 | * @param err_msg NULL if successful, otherwise contains error message | ||
544 | */ | ||
545 | static void | ||
546 | process_hello_retry_send_key (void *cls, const struct GNUNET_PeerIdentity *peer, | ||
547 | const struct GNUNET_HELLO_Message *hello, | ||
548 | const char *err_msg) | ||
549 | { | ||
550 | struct Neighbour *n = cls; | ||
551 | |||
552 | if (err_msg != NULL) | ||
553 | { | ||
554 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
555 | _("Error in communication with PEERINFO service\n")); | ||
556 | /* return; */ | ||
557 | } | ||
558 | |||
559 | if (peer == NULL) | ||
560 | { | ||
561 | #if DEBUG_CORE | ||
562 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Entered `%s' and `%s' is NULL!\n", | ||
563 | "process_hello_retry_send_key", "peer"); | ||
564 | #endif | ||
565 | n->pitr = NULL; | ||
566 | if (n->public_key != NULL) | ||
567 | { | ||
568 | if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) | ||
569 | { | ||
570 | GNUNET_SCHEDULER_cancel (n->retry_set_key_task); | ||
571 | n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; | ||
572 | } | ||
573 | GNUNET_STATISTICS_update (stats, | ||
574 | gettext_noop | ||
575 | ("# SET_KEY messages deferred (need public key)"), | ||
576 | -1, GNUNET_NO); | ||
577 | send_key (n); | ||
578 | } | ||
579 | else | ||
580 | { | ||
581 | #if DEBUG_CORE | ||
582 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
583 | "Failed to obtain public key for peer `%4s', delaying processing of SET_KEY\n", | ||
584 | GNUNET_i2s (&n->peer)); | ||
585 | #endif | ||
586 | GNUNET_STATISTICS_update (stats, | ||
587 | gettext_noop | ||
588 | ("# Delayed connecting due to lack of public key"), | ||
589 | 1, GNUNET_NO); | ||
590 | if (GNUNET_SCHEDULER_NO_TASK == n->retry_set_key_task) | ||
591 | n->retry_set_key_task = | ||
592 | GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency, | ||
593 | &set_key_retry_task, n); | ||
594 | } | ||
595 | return; | ||
596 | } | ||
597 | |||
598 | #if DEBUG_CORE | ||
599 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Entered `%s' for peer `%4s'\n", | ||
600 | "process_hello_retry_send_key", GNUNET_i2s (peer)); | ||
601 | #endif | ||
602 | if (n->public_key != NULL) | ||
603 | { | ||
604 | /* already have public key, why are we here? */ | ||
605 | GNUNET_break (0); | ||
606 | return; | ||
607 | } | ||
608 | |||
609 | #if DEBUG_CORE | ||
610 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
611 | "Received new `%s' message for `%4s', initiating key exchange.\n", | ||
612 | "HELLO", GNUNET_i2s (peer)); | ||
613 | #endif | ||
614 | n->public_key = | ||
615 | GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); | ||
616 | if (GNUNET_OK != GNUNET_HELLO_get_key (hello, n->public_key)) | ||
617 | { | ||
618 | GNUNET_STATISTICS_update (stats, | ||
619 | gettext_noop | ||
620 | ("# Error extracting public key from HELLO"), 1, | ||
621 | GNUNET_NO); | ||
622 | GNUNET_free (n->public_key); | ||
623 | n->public_key = NULL; | ||
624 | #if DEBUG_CORE | ||
625 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
626 | "GNUNET_HELLO_get_key returned awfully\n"); | ||
627 | #endif | ||
628 | return; | ||
629 | } | ||
630 | } | ||
631 | |||
632 | |||
633 | /** | ||
634 | * Send our key (and encrypted PING) to the other peer. | ||
635 | * | ||
636 | * @param n the other peer | ||
637 | */ | ||
638 | static void | ||
639 | send_key (struct Neighbour *n) | ||
640 | { | ||
641 | struct MessageEntry *pos; | ||
642 | struct SetKeyMessage *sm; | ||
643 | struct MessageEntry *me; | ||
644 | struct PingMessage pp; | ||
645 | struct PingMessage *pm; | ||
646 | struct GNUNET_CRYPTO_AesInitializationVector iv; | ||
647 | |||
648 | if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) | ||
649 | { | ||
650 | GNUNET_SCHEDULER_cancel (n->retry_set_key_task); | ||
651 | n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; | ||
652 | } | ||
653 | if (n->pitr != NULL) | ||
654 | { | ||
655 | #if DEBUG_CORE | ||
656 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
657 | "Key exchange in progress with `%4s'.\n", | ||
658 | GNUNET_i2s (&n->peer)); | ||
659 | #endif | ||
660 | return; /* already in progress */ | ||
661 | } | ||
662 | if (GNUNET_YES != n->is_connected) | ||
663 | { | ||
664 | GNUNET_STATISTICS_update (stats, | ||
665 | gettext_noop | ||
666 | ("# Asking transport to connect (for SET_KEY)"), | ||
667 | 1, GNUNET_NO); | ||
668 | GNUNET_TRANSPORT_try_connect (transport, &n->peer); | ||
669 | return; | ||
670 | } | ||
671 | #if DEBUG_CORE | ||
672 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
673 | "Asked to perform key exchange with `%4s'.\n", | ||
674 | GNUNET_i2s (&n->peer)); | ||
675 | #endif | ||
676 | if (n->public_key == NULL) | ||
677 | { | ||
678 | /* lookup n's public key, then try again */ | ||
679 | #if DEBUG_CORE | ||
680 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
681 | "Lacking public key for `%4s', trying to obtain one (send_key).\n", | ||
682 | GNUNET_i2s (&n->peer)); | ||
683 | #endif | ||
684 | GNUNET_assert (n->pitr == NULL); | ||
685 | n->pitr = | ||
686 | GNUNET_PEERINFO_iterate (peerinfo, &n->peer, | ||
687 | GNUNET_TIME_relative_multiply | ||
688 | (GNUNET_TIME_UNIT_SECONDS, 20), | ||
689 | &process_hello_retry_send_key, n); | ||
690 | return; | ||
691 | } | ||
692 | pos = n->encrypted_head; | ||
693 | while (pos != NULL) | ||
694 | { | ||
695 | if (GNUNET_YES == pos->is_setkey) | ||
696 | { | ||
697 | if (pos->sender_status == n->status) | ||
698 | { | ||
699 | #if DEBUG_CORE | ||
700 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
701 | "`%s' message for `%4s' queued already\n", "SET_KEY", | ||
702 | GNUNET_i2s (&n->peer)); | ||
703 | #endif | ||
704 | goto trigger_processing; | ||
705 | } | ||
706 | GNUNET_CONTAINER_DLL_remove (n->encrypted_head, n->encrypted_tail, pos); | ||
707 | GNUNET_free (pos); | ||
708 | #if DEBUG_CORE | ||
709 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
710 | "Removing queued `%s' message for `%4s', will create a new one\n", | ||
711 | "SET_KEY", GNUNET_i2s (&n->peer)); | ||
712 | #endif | ||
713 | break; | ||
714 | } | ||
715 | pos = pos->next; | ||
716 | } | ||
717 | |||
718 | /* update status */ | ||
719 | switch (n->status) | ||
720 | { | ||
721 | case PEER_STATE_DOWN: | ||
722 | n->status = PEER_STATE_KEY_SENT; | ||
723 | break; | ||
724 | case PEER_STATE_KEY_SENT: | ||
725 | break; | ||
726 | case PEER_STATE_KEY_RECEIVED: | ||
727 | break; | ||
728 | case PEER_STATE_KEY_CONFIRMED: | ||
729 | break; | ||
730 | default: | ||
731 | GNUNET_break (0); | ||
732 | break; | ||
733 | } | ||
734 | |||
735 | |||
736 | /* first, set key message */ | ||
737 | me = GNUNET_malloc (sizeof (struct MessageEntry) + | ||
738 | sizeof (struct SetKeyMessage) + | ||
739 | sizeof (struct PingMessage)); | ||
740 | me->deadline = GNUNET_TIME_relative_to_absolute (MAX_SET_KEY_DELAY); | ||
741 | me->priority = SET_KEY_PRIORITY; | ||
742 | me->size = sizeof (struct SetKeyMessage) + sizeof (struct PingMessage); | ||
743 | me->is_setkey = GNUNET_YES; | ||
744 | me->got_slack = GNUNET_YES; /* do not defer this one! */ | ||
745 | me->sender_status = n->status; | ||
746 | GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head, n->encrypted_tail, | ||
747 | n->encrypted_tail, me); | ||
748 | sm = (struct SetKeyMessage *) &me[1]; | ||
749 | sm->header.size = htons (sizeof (struct SetKeyMessage)); | ||
750 | sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SET_KEY); | ||
751 | sm->sender_status = | ||
752 | htonl ((int32_t) | ||
753 | ((n->status == | ||
754 | PEER_STATE_DOWN) ? PEER_STATE_KEY_SENT : n->status)); | ||
755 | sm->purpose.size = | ||
756 | htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + | ||
757 | sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
758 | sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) + | ||
759 | sizeof (struct GNUNET_PeerIdentity)); | ||
760 | sm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_KEY); | ||
761 | sm->creation_time = GNUNET_TIME_absolute_hton (n->encrypt_key_created); | ||
762 | sm->target = n->peer; | ||
763 | GNUNET_assert (GNUNET_OK == | ||
764 | GNUNET_CRYPTO_rsa_encrypt (&n->encrypt_key, | ||
765 | sizeof (struct | ||
766 | GNUNET_CRYPTO_AesSessionKey), | ||
767 | n->public_key, &sm->encrypted_key)); | ||
768 | GNUNET_assert (GNUNET_OK == | ||
769 | GNUNET_CRYPTO_rsa_sign (my_private_key, &sm->purpose, | ||
770 | &sm->signature)); | ||
771 | pm = (struct PingMessage *) &sm[1]; | ||
772 | pm->header.size = htons (sizeof (struct PingMessage)); | ||
773 | pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING); | ||
774 | pm->iv_seed = | ||
775 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); | ||
776 | derive_iv (&iv, &n->encrypt_key, pm->iv_seed, &n->peer); | ||
777 | pp.challenge = n->ping_challenge; | ||
778 | pp.target = n->peer; | ||
779 | #if DEBUG_HANDSHAKE | ||
780 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
781 | "Encrypting `%s' and `%s' messages with challenge %u for `%4s' using key %u, IV %u (salt %u).\n", | ||
782 | "SET_KEY", "PING", (unsigned int) n->ping_challenge, | ||
783 | GNUNET_i2s (&n->peer), (unsigned int) n->encrypt_key.crc32, | ||
784 | GNUNET_CRYPTO_crc32_n (&iv, sizeof (iv)), pm->iv_seed); | ||
785 | #endif | ||
786 | do_encrypt (n, &iv, &pp.target, &pm->target, | ||
787 | sizeof (struct PingMessage) - ((void *) &pm->target - | ||
788 | (void *) pm)); | ||
789 | GNUNET_STATISTICS_update (stats, | ||
790 | gettext_noop | ||
791 | ("# SET_KEY and PING messages created"), 1, | ||
792 | GNUNET_NO); | ||
793 | #if DEBUG_CORE | ||
794 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
795 | "Have %llu ms left for `%s' transmission.\n", | ||
796 | (unsigned long long) | ||
797 | GNUNET_TIME_absolute_get_remaining (me->deadline).rel_value, | ||
798 | "SET_KEY"); | ||
799 | #endif | ||
800 | trigger_processing: | ||
801 | /* trigger queue processing */ | ||
802 | process_encrypted_neighbour_queue (n); | ||
803 | if ((n->status != PEER_STATE_KEY_CONFIRMED) && | ||
804 | (GNUNET_SCHEDULER_NO_TASK == n->retry_set_key_task)) | ||
805 | n->retry_set_key_task = | ||
806 | GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency, | ||
807 | &set_key_retry_task, n); | ||
808 | } | ||
809 | |||
810 | |||
811 | /** | ||
812 | * We received a SET_KEY message. Validate and update | ||
813 | * our key material and status. | ||
814 | * | ||
815 | * @param n the neighbour from which we received message m | ||
816 | * @param m the set key message we received | ||
817 | * @param ats performance data | ||
818 | * @param ats_count number of entries in ats (excluding 0-termination) | ||
819 | */ | ||
820 | static void | ||
821 | handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m, | ||
822 | const struct GNUNET_TRANSPORT_ATS_Information *ats, | ||
823 | uint32_t ats_count); | ||
824 | |||
825 | |||
826 | |||
827 | /** | ||
828 | * PEERINFO is giving us a HELLO for a peer. Add the public key to | ||
829 | * the neighbour's struct and retry handling the set_key message. Or, | ||
830 | * if we did not get a HELLO, just free the set key message. | ||
831 | * | ||
832 | * @param cls pointer to the set key message | ||
833 | * @param peer the peer for which this is the HELLO | ||
834 | * @param hello HELLO message of that peer | ||
835 | * @param err_msg NULL if successful, otherwise contains error message | ||
836 | */ | ||
837 | static void | ||
838 | process_hello_retry_handle_set_key (void *cls, | ||
839 | const struct GNUNET_PeerIdentity *peer, | ||
840 | const struct GNUNET_HELLO_Message *hello, | ||
841 | const char *err_msg) | ||
842 | { | ||
843 | struct Neighbour *n = cls; | ||
844 | struct SetKeyMessage *sm = n->skm; | ||
845 | |||
846 | if (err_msg != NULL) | ||
847 | { | ||
848 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
849 | _("Error in communication with PEERINFO service\n")); | ||
850 | /* return; */ | ||
851 | } | ||
852 | |||
853 | if (peer == NULL) | ||
854 | { | ||
855 | n->skm = NULL; | ||
856 | n->pitr = NULL; | ||
857 | if (n->public_key != NULL) | ||
858 | { | ||
859 | #if DEBUG_CORE | ||
860 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
861 | "Received `%s' for `%4s', continuing processing of `%s' message.\n", | ||
862 | "HELLO", GNUNET_i2s (&n->peer), "SET_KEY"); | ||
863 | #endif | ||
864 | handle_set_key (n, sm, NULL, 0); | ||
865 | } | ||
866 | else | ||
867 | { | ||
868 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
869 | _ | ||
870 | ("Ignoring `%s' message due to lack of public key for peer `%4s' (failed to obtain one).\n"), | ||
871 | "SET_KEY", GNUNET_i2s (&n->peer)); | ||
872 | } | ||
873 | GNUNET_free (sm); | ||
874 | return; | ||
875 | } | ||
876 | if (n->public_key != NULL) | ||
877 | return; /* multiple HELLOs match!? */ | ||
878 | n->public_key = | ||
879 | GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); | ||
880 | if (GNUNET_OK != GNUNET_HELLO_get_key (hello, n->public_key)) | ||
881 | { | ||
882 | GNUNET_break_op (0); | ||
883 | GNUNET_free (n->public_key); | ||
884 | n->public_key = NULL; | ||
885 | } | ||
886 | } | ||
887 | |||
888 | |||
889 | |||
890 | /** | ||
891 | * Task that will retry "send_key" if our previous attempt failed | ||
892 | * to yield a PONG. | ||
893 | */ | ||
894 | static void | ||
895 | set_key_retry_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
896 | { | ||
897 | struct Neighbour *n = cls; | ||
898 | |||
899 | #if DEBUG_CORE | ||
900 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Retrying key transmission to `%4s'\n", | ||
901 | GNUNET_i2s (&n->peer)); | ||
902 | #endif | ||
903 | n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; | ||
904 | n->set_key_retry_frequency = | ||
905 | GNUNET_TIME_relative_multiply (n->set_key_retry_frequency, 2); | ||
906 | send_key (n); | ||
907 | } | ||
908 | |||
909 | |||
910 | struct GSC_KeyExchangeInfo * | ||
911 | GSC_KX_start (const struct GNUNET_PeerIdentity *pid) | ||
912 | { | ||
913 | struct GSC_KeyExchangeInfo *kx; | ||
914 | |||
915 | kx = NULL; | ||
916 | return kx; | ||
917 | } | ||
918 | |||
919 | |||
920 | void | ||
921 | GSC_KX_stop (struct GSC_KeyExchangeInfo *kx) | ||
922 | { | ||
923 | if (kx->pitr != NULL) | ||
924 | { | ||
925 | GNUNET_PEERINFO_iterate_cancel (kx->pitr); | ||
926 | kx->pitr = NULL; | ||
927 | } | ||
928 | if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) | ||
929 | GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); | ||
930 | GNUNET_free_non_null (kx->public_key); | ||
931 | GNUNET_free (kx); | ||
932 | } | ||
933 | |||
934 | |||
935 | int | ||
936 | GSC_KX_init () | ||
937 | { | ||
938 | peerinfo = GNUNET_PEERINFO_connect (cfg); | ||
939 | if (NULL == peerinfo) | ||
940 | { | ||
941 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
942 | _("Could not access PEERINFO service. Exiting.\n")); | ||
943 | return GNUNET_SYSERR; | ||
944 | } | ||
945 | return GNUNET_OK; | ||
946 | } | ||
947 | |||
948 | |||
949 | void | ||
950 | GSC_KX_done () | ||
951 | { | ||
952 | if (peerinfo != NULL) | ||
953 | { | ||
954 | GNUNET_PEERINFO_disconnect (peerinfo); | ||
955 | peerinfo = NULL; | ||
956 | } | ||
957 | |||
958 | } | ||
diff --git a/src/core/gnunet-service-core_kx.h b/src/core/gnunet-service-core_kx.h new file mode 100644 index 000000000..f4f1daaeb --- /dev/null +++ b/src/core/gnunet-service-core_kx.h | |||
@@ -0,0 +1,77 @@ | |||
1 | struct GSC_KeyExchangeInfo | ||
2 | { | ||
3 | |||
4 | /** | ||
5 | * SetKeyMessage to transmit, NULL if we are not currently trying | ||
6 | * to send one. | ||
7 | */ | ||
8 | struct SetKeyMessage *skm; | ||
9 | |||
10 | /** | ||
11 | * Non-NULL if we are currently looking up HELLOs for this peer. | ||
12 | * for this peer. | ||
13 | */ | ||
14 | struct GNUNET_PEERINFO_IteratorContext *pitr; | ||
15 | |||
16 | /** | ||
17 | * Public key of the neighbour, NULL if we don't have it yet. | ||
18 | */ | ||
19 | struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key; | ||
20 | |||
21 | /** | ||
22 | * We received a PING message before we got the "public_key" | ||
23 | * (or the SET_KEY). We keep it here until we have a key | ||
24 | * to decrypt it. NULL if no PING is pending. | ||
25 | */ | ||
26 | struct PingMessage *pending_ping; | ||
27 | |||
28 | /** | ||
29 | * We received a PONG message before we got the "public_key" | ||
30 | * (or the SET_KEY). We keep it here until we have a key | ||
31 | * to decrypt it. NULL if no PONG is pending. | ||
32 | */ | ||
33 | struct PongMessage *pending_pong; | ||
34 | |||
35 | /** | ||
36 | * Key we use to encrypt our messages for the other peer | ||
37 | * (initialized by us when we do the handshake). | ||
38 | */ | ||
39 | struct GNUNET_CRYPTO_AesSessionKey encrypt_key; | ||
40 | |||
41 | /** | ||
42 | * Key we use to decrypt messages from the other peer | ||
43 | * (given to us by the other peer during the handshake). | ||
44 | */ | ||
45 | struct GNUNET_CRYPTO_AesSessionKey decrypt_key; | ||
46 | |||
47 | /** | ||
48 | * At what time did we generate our encryption key? | ||
49 | */ | ||
50 | struct GNUNET_TIME_Absolute encrypt_key_created; | ||
51 | |||
52 | /** | ||
53 | * At what time did the other peer generate the decryption key? | ||
54 | */ | ||
55 | struct GNUNET_TIME_Absolute decrypt_key_created; | ||
56 | |||
57 | /** | ||
58 | * At what frequency are we currently re-trying SET_KEY messages? | ||
59 | */ | ||
60 | struct GNUNET_TIME_Relative set_key_retry_frequency; | ||
61 | |||
62 | /** | ||
63 | * ID of task used for re-trying SET_KEY and PING message. | ||
64 | */ | ||
65 | GNUNET_SCHEDULER_TaskIdentifier retry_set_key_task; | ||
66 | |||
67 | /** | ||
68 | * What was our PING challenge number (for this peer)? | ||
69 | */ | ||
70 | uint32_t ping_challenge; | ||
71 | |||
72 | /** | ||
73 | * What is our connection status? | ||
74 | */ | ||
75 | enum PeerStateMachine status; | ||
76 | |||
77 | }; | ||
diff --git a/src/core/gnunet-service-core_neighbours.c b/src/core/gnunet-service-core_neighbours.c new file mode 100644 index 000000000..12d002da8 --- /dev/null +++ b/src/core/gnunet-service-core_neighbours.c | |||
@@ -0,0 +1,617 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | 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 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core_neighbours.c | ||
23 | * @brief code for managing low-level 'plaintext' connections with transport (key exchange may or may not be done yet) | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_transport_service.h" | ||
29 | #include "gnunet_service_core.h" | ||
30 | #include "gnunet_service_core-neighbours.h" | ||
31 | #include "gnunet_service_core-kx.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Message ready for transmission via transport service. This struct | ||
36 | * is followed by the actual content of the message. | ||
37 | */ | ||
38 | struct MessageEntry | ||
39 | { | ||
40 | |||
41 | /** | ||
42 | * We keep messages in a doubly linked list. | ||
43 | */ | ||
44 | struct MessageEntry *next; | ||
45 | |||
46 | /** | ||
47 | * We keep messages in a doubly linked list. | ||
48 | */ | ||
49 | struct MessageEntry *prev; | ||
50 | |||
51 | /** | ||
52 | * By when are we supposed to transmit this message? | ||
53 | */ | ||
54 | struct GNUNET_TIME_Absolute deadline; | ||
55 | |||
56 | /** | ||
57 | * How long is the message? (number of bytes following the "struct | ||
58 | * MessageEntry", but not including the size of "struct | ||
59 | * MessageEntry" itself!) | ||
60 | */ | ||
61 | size_t size; | ||
62 | |||
63 | }; | ||
64 | |||
65 | |||
66 | /** | ||
67 | * Data kept per transport-connected peer. | ||
68 | */ | ||
69 | struct Neighbour | ||
70 | { | ||
71 | |||
72 | /** | ||
73 | * Head of the batched message queue (already ordered, transmit | ||
74 | * starting with the head). | ||
75 | */ | ||
76 | struct MessageEntry *message_head; | ||
77 | |||
78 | /** | ||
79 | * Tail of the batched message queue (already ordered, append new | ||
80 | * messages to tail). | ||
81 | */ | ||
82 | struct MessageEntry *message_tail; | ||
83 | |||
84 | /** | ||
85 | * Handle for pending requests for transmission to this peer | ||
86 | * with the transport service. NULL if no request is pending. | ||
87 | */ | ||
88 | struct GNUNET_TRANSPORT_TransmitHandle *th; | ||
89 | |||
90 | /** | ||
91 | * Information about the key exchange with the other peer. | ||
92 | */ | ||
93 | struct GSC_KeyExchangeInfo *kxinfo; | ||
94 | |||
95 | /** | ||
96 | * Identity of the other peer. | ||
97 | */ | ||
98 | struct GNUNET_PeerIdentity peer; | ||
99 | |||
100 | /** | ||
101 | * ID of task used for re-trying plaintext scheduling. | ||
102 | */ | ||
103 | GNUNET_SCHEDULER_TaskIdentifier retry_plaintext_task; | ||
104 | |||
105 | /** | ||
106 | * Tracking bandwidth for sending to this peer. | ||
107 | */ | ||
108 | struct GNUNET_BANDWIDTH_Tracker available_send_window; | ||
109 | |||
110 | /** | ||
111 | * Tracking bandwidth for sending to this peer. | ||
112 | */ | ||
113 | struct GNUNET_BANDWIDTH_Tracker available_recv_window; | ||
114 | |||
115 | |||
116 | }; | ||
117 | |||
118 | |||
119 | /** | ||
120 | * Map of peer identities to 'struct Neighbour'. | ||
121 | */ | ||
122 | static struct GNUNET_CONTAINER_MultiHashMap *neighbours; | ||
123 | |||
124 | /** | ||
125 | * Transport service. | ||
126 | */ | ||
127 | static struct GNUNET_TRANSPORT_Handle *transport; | ||
128 | |||
129 | |||
130 | |||
131 | /** | ||
132 | * Find the entry for the given neighbour. | ||
133 | * | ||
134 | * @param peer identity of the neighbour | ||
135 | * @return NULL if we are not connected, otherwise the | ||
136 | * neighbour's entry. | ||
137 | */ | ||
138 | static struct Neighbour * | ||
139 | find_neighbour (const struct GNUNET_PeerIdentity *peer) | ||
140 | { | ||
141 | return GNUNET_CONTAINER_multihashmap_get (neighbours, &peer->hashPubKey); | ||
142 | } | ||
143 | |||
144 | |||
145 | /** | ||
146 | * Free the given entry for the neighbour. | ||
147 | * | ||
148 | * @param n neighbour to free | ||
149 | */ | ||
150 | static void | ||
151 | free_neighbour (struct Neighbour *n) | ||
152 | { | ||
153 | struct MessageEntry *m; | ||
154 | |||
155 | #if DEBUG_CORE | ||
156 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
157 | "Destroying neighbour entry for peer `%4s'\n", | ||
158 | GNUNET_i2s (&n->peer)); | ||
159 | #endif | ||
160 | while (NULL != (m = n->message_head)) | ||
161 | { | ||
162 | GNUNET_CONTAINER_DLL_remove (n->message_head, n->message_tail, m); | ||
163 | GNUNET_free (m); | ||
164 | } | ||
165 | if (NULL != n->th) | ||
166 | { | ||
167 | GNUNET_TRANSPORT_notify_transmit_ready_cancel (n->th); | ||
168 | n->th = NULL; | ||
169 | } | ||
170 | if (NULL != n->kx) | ||
171 | { | ||
172 | GSC_KX_stop (n->kx); | ||
173 | n->kx = NULL; | ||
174 | } | ||
175 | if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_TASK) | ||
176 | { | ||
177 | GNUNET_SCHEDULER_cancel (n->retry_plaintext_task); | ||
178 | n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK; | ||
179 | } | ||
180 | GNUNET_assert (GNUNET_OK == | ||
181 | GNUNET_CONTAINER_multihashmap_remove (neighbours, | ||
182 | &n->peer.hashPubKey, n)); | ||
183 | GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), | ||
184 | GNUNET_CONTAINER_multihashmap_size (neighbours), | ||
185 | GNUNET_NO); | ||
186 | GNUNET_free (n); | ||
187 | } | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Check if we have encrypted messages for the specified neighbour | ||
192 | * pending, and if so, check with the transport about sending them | ||
193 | * out. | ||
194 | * | ||
195 | * @param n neighbour to check. | ||
196 | */ | ||
197 | static void | ||
198 | process_queue (struct Neighbour *n); | ||
199 | |||
200 | |||
201 | /** | ||
202 | * Function called when the transport service is ready to receive a | ||
203 | * message for the respective peer | ||
204 | * | ||
205 | * @param cls neighbour to use message from | ||
206 | * @param size number of bytes we can transmit | ||
207 | * @param buf where to copy the message | ||
208 | * @return number of bytes transmitted | ||
209 | */ | ||
210 | static size_t | ||
211 | transmit_ready (void *cls, size_t size, void *buf) | ||
212 | { | ||
213 | struct Neighbour *n = cls; | ||
214 | struct MessageEntry *m; | ||
215 | size_t ret; | ||
216 | char *cbuf; | ||
217 | |||
218 | n->th = NULL; | ||
219 | m = n->message_head; | ||
220 | if (m == NULL) | ||
221 | { | ||
222 | #if DEBUG_CORE | ||
223 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
224 | "Encrypted message queue empty, no messages added to buffer for `%4s'\n", | ||
225 | GNUNET_i2s (&n->peer)); | ||
226 | #endif | ||
227 | return 0; | ||
228 | } | ||
229 | GNUNET_CONTAINER_DLL_remove (n->encrypted_head, n->encrypted_tail, m); | ||
230 | if (buf == NULL) | ||
231 | { | ||
232 | #if DEBUG_CORE | ||
233 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
234 | "Transmission of message of type %u and size %u failed\n", | ||
235 | (unsigned int) | ||
236 | ntohs (((struct GNUNET_MessageHeader *) &m[1])->type), | ||
237 | (unsigned int) m->size); | ||
238 | #endif | ||
239 | GNUNET_free (m); | ||
240 | process_queue (n); | ||
241 | return 0; | ||
242 | } | ||
243 | ret = 0; | ||
244 | cbuf = buf; | ||
245 | GNUNET_assert (size >= m->size); | ||
246 | memcpy (cbuf, &m[1], m->size); | ||
247 | ret = m->size; | ||
248 | GNUNET_BANDWIDTH_tracker_consume (&n->available_send_window, m->size); | ||
249 | #if DEBUG_CORE | ||
250 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
251 | "Copied message of type %u and size %u into transport buffer for `%4s'\n", | ||
252 | (unsigned int) | ||
253 | ntohs (((struct GNUNET_MessageHeader *) &m[1])->type), | ||
254 | (unsigned int) ret, GNUNET_i2s (&n->peer)); | ||
255 | #endif | ||
256 | GNUNET_free (m); | ||
257 | process_queue (n); | ||
258 | GNUNET_STATISTICS_update (GSC_stats, | ||
259 | gettext_noop | ||
260 | ("# encrypted bytes given to transport"), ret, | ||
261 | GNUNET_NO); | ||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | |||
266 | /** | ||
267 | * Check if we have messages for the specified neighbour pending, and | ||
268 | * if so, check with the transport about sending them out. | ||
269 | * | ||
270 | * @param n neighbour to check. | ||
271 | */ | ||
272 | static void | ||
273 | process_queue (struct Neighbour *n) | ||
274 | { | ||
275 | struct MessageEntry *m; | ||
276 | |||
277 | if (n->th != NULL) | ||
278 | return; /* request already pending */ | ||
279 | m = n->message_head; | ||
280 | if (m == NULL) | ||
281 | return; | ||
282 | #if DEBUG_CORE > 1 | ||
283 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
284 | "Asking transport for transmission of %u bytes to `%4s' in next %llu ms\n", | ||
285 | (unsigned int) m->size, GNUNET_i2s (&n->peer), | ||
286 | (unsigned long long) | ||
287 | GNUNET_TIME_absolute_get_remaining (m->deadline).rel_value); | ||
288 | #endif | ||
289 | n->th = | ||
290 | GNUNET_TRANSPORT_notify_transmit_ready (transport, &n->peer, m->size, | ||
291 | m->priority, | ||
292 | GNUNET_TIME_absolute_get_remaining | ||
293 | (m->deadline), | ||
294 | &transmit_ready, | ||
295 | n); | ||
296 | if (n->th != NULL) | ||
297 | return; | ||
298 | /* message request too large or duplicate request */ | ||
299 | GNUNET_break (0); | ||
300 | /* discard encrypted message */ | ||
301 | GNUNET_CONTAINER_DLL_remove (n->encrypted_head, n->encrypted_tail, m); | ||
302 | GNUNET_free (m); | ||
303 | process_queue (n); | ||
304 | } | ||
305 | |||
306 | |||
307 | |||
308 | /** | ||
309 | * Function called by transport to notify us that | ||
310 | * a peer connected to us (on the network level). | ||
311 | * | ||
312 | * @param cls closure | ||
313 | * @param peer the peer that connected | ||
314 | * @param ats performance data | ||
315 | * @param ats_count number of entries in ats (excluding 0-termination) | ||
316 | */ | ||
317 | static void | ||
318 | handle_transport_notify_connect (void *cls, | ||
319 | const struct GNUNET_PeerIdentity *peer, | ||
320 | const struct GNUNET_TRANSPORT_ATS_Information | ||
321 | *ats, uint32_t ats_count) | ||
322 | { | ||
323 | struct Neighbour *n; | ||
324 | |||
325 | if (0 == memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))) | ||
326 | { | ||
327 | GNUNET_break (0); | ||
328 | return; | ||
329 | } | ||
330 | n = find_neighbour (peer); | ||
331 | if (n != NULL) | ||
332 | { | ||
333 | /* duplicate connect notification!? */ | ||
334 | GNUNET_break (0); | ||
335 | return; | ||
336 | } | ||
337 | #if DEBUG_CORE | ||
338 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received connection from `%4s'.\n", | ||
339 | GNUNET_i2s (peer)); | ||
340 | #endif | ||
341 | n = GNUNET_malloc (sizeof (struct Neighbour)); | ||
342 | n->peer = *pid; | ||
343 | GNUNET_BANDWIDTH_tracker_init (&n->available_send_window, | ||
344 | GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT, | ||
345 | MAX_WINDOW_TIME_S); | ||
346 | GNUNET_BANDWIDTH_tracker_init (&n->available_recv_window, | ||
347 | GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT, | ||
348 | MAX_WINDOW_TIME_S); | ||
349 | GNUNET_assert (GNUNET_OK == | ||
350 | GNUNET_CONTAINER_multihashmap_put (neighbours, | ||
351 | &n->peer.hashPubKey, n, | ||
352 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
353 | GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), | ||
354 | GNUNET_CONTAINER_multihashmap_size (neighbours), | ||
355 | GNUNET_NO); | ||
356 | GNUNET_TRANSPORT_set_quota (transport, peer, | ||
357 | GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT, | ||
358 | GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT); | ||
359 | n->kx = GSC_KX_start (pid); | ||
360 | } | ||
361 | |||
362 | |||
363 | /** | ||
364 | * Function called by transport telling us that a peer | ||
365 | * disconnected. | ||
366 | * | ||
367 | * @param cls closure | ||
368 | * @param peer the peer that disconnected | ||
369 | */ | ||
370 | static void | ||
371 | handle_transport_notify_disconnect (void *cls, | ||
372 | const struct GNUNET_PeerIdentity *peer) | ||
373 | { | ||
374 | struct Neighbour *n; | ||
375 | |||
376 | #if DEBUG_CORE | ||
377 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
378 | "Peer `%4s' disconnected from us; received notification from transport.\n", | ||
379 | GNUNET_i2s (peer)); | ||
380 | #endif | ||
381 | n = find_neighbour (peer); | ||
382 | if (n == NULL) | ||
383 | { | ||
384 | GNUNET_break (0); | ||
385 | return; | ||
386 | } | ||
387 | free_neighbour (n); | ||
388 | } | ||
389 | |||
390 | |||
391 | /** | ||
392 | * Function called by the transport for each received message. | ||
393 | * | ||
394 | * @param cls closure | ||
395 | * @param peer (claimed) identity of the other peer | ||
396 | * @param message the message | ||
397 | * @param ats performance data | ||
398 | * @param ats_count number of entries in ats (excluding 0-termination) | ||
399 | */ | ||
400 | static void | ||
401 | handle_transport_receive (void *cls, const struct GNUNET_PeerIdentity *peer, | ||
402 | const struct GNUNET_MessageHeader *message, | ||
403 | const struct GNUNET_TRANSPORT_ATS_Information *ats, | ||
404 | uint32_t ats_count) | ||
405 | { | ||
406 | struct Neighbour *n; | ||
407 | struct GNUNET_TIME_Absolute now; | ||
408 | int up; | ||
409 | uint16_t type; | ||
410 | uint16_t size; | ||
411 | int changed; | ||
412 | |||
413 | #if DEBUG_CORE > 1 | ||
414 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
415 | "Received message of type %u from `%4s', demultiplexing.\n", | ||
416 | (unsigned int) ntohs (message->type), GNUNET_i2s (peer)); | ||
417 | #endif | ||
418 | if (0 == memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity))) | ||
419 | { | ||
420 | GNUNET_break (0); | ||
421 | return; | ||
422 | } | ||
423 | n = find_neighbour (peer); | ||
424 | if (n == NULL) | ||
425 | { | ||
426 | /* received message from peer that is not connected!? */ | ||
427 | GNUNET_break (0); | ||
428 | return; | ||
429 | } | ||
430 | |||
431 | |||
432 | changed = GNUNET_NO; | ||
433 | up = (n->status == PEER_STATE_KEY_CONFIRMED); | ||
434 | type = ntohs (message->type); | ||
435 | size = ntohs (message->size); | ||
436 | switch (type) | ||
437 | { | ||
438 | case GNUNET_MESSAGE_TYPE_CORE_SET_KEY: | ||
439 | if (size != sizeof (struct SetKeyMessage)) | ||
440 | { | ||
441 | GNUNET_break_op (0); | ||
442 | return; | ||
443 | } | ||
444 | GNUNET_STATISTICS_update (stats, gettext_noop ("# session keys received"), | ||
445 | 1, GNUNET_NO); | ||
446 | handle_set_key (n, (const struct SetKeyMessage *) message, ats, ats_count); | ||
447 | break; | ||
448 | case GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE: | ||
449 | if (size < | ||
450 | sizeof (struct EncryptedMessage) + sizeof (struct GNUNET_MessageHeader)) | ||
451 | { | ||
452 | GNUNET_break_op (0); | ||
453 | return; | ||
454 | } | ||
455 | if ((n->status != PEER_STATE_KEY_RECEIVED) && | ||
456 | (n->status != PEER_STATE_KEY_CONFIRMED)) | ||
457 | { | ||
458 | GNUNET_STATISTICS_update (stats, | ||
459 | gettext_noop | ||
460 | ("# failed to decrypt message (no session key)"), | ||
461 | 1, GNUNET_NO); | ||
462 | send_key (n); | ||
463 | return; | ||
464 | } | ||
465 | handle_encrypted_message (n, (const struct EncryptedMessage *) message, ats, | ||
466 | ats_count); | ||
467 | break; | ||
468 | case GNUNET_MESSAGE_TYPE_CORE_PING: | ||
469 | if (size != sizeof (struct PingMessage)) | ||
470 | { | ||
471 | GNUNET_break_op (0); | ||
472 | return; | ||
473 | } | ||
474 | GNUNET_STATISTICS_update (stats, gettext_noop ("# PING messages received"), | ||
475 | 1, GNUNET_NO); | ||
476 | if ((n->status != PEER_STATE_KEY_RECEIVED) && | ||
477 | (n->status != PEER_STATE_KEY_CONFIRMED)) | ||
478 | { | ||
479 | #if DEBUG_CORE > 1 | ||
480 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
481 | "Core service receives `%s' request from `%4s' but have not processed key; marking as pending.\n", | ||
482 | "PING", GNUNET_i2s (&n->peer)); | ||
483 | #endif | ||
484 | GNUNET_free_non_null (n->pending_ping); | ||
485 | n->pending_ping = GNUNET_malloc (sizeof (struct PingMessage)); | ||
486 | memcpy (n->pending_ping, message, sizeof (struct PingMessage)); | ||
487 | return; | ||
488 | } | ||
489 | handle_ping (n, (const struct PingMessage *) message, ats, ats_count); | ||
490 | break; | ||
491 | case GNUNET_MESSAGE_TYPE_CORE_PONG: | ||
492 | if (size != sizeof (struct PongMessage)) | ||
493 | { | ||
494 | GNUNET_break_op (0); | ||
495 | return; | ||
496 | } | ||
497 | GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages received"), | ||
498 | 1, GNUNET_NO); | ||
499 | if ((n->status != PEER_STATE_KEY_RECEIVED) && | ||
500 | (n->status != PEER_STATE_KEY_CONFIRMED)) | ||
501 | { | ||
502 | #if DEBUG_CORE > 1 | ||
503 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
504 | "Core service receives `%s' request from `%4s' but have not processed key; marking as pending.\n", | ||
505 | "PONG", GNUNET_i2s (&n->peer)); | ||
506 | #endif | ||
507 | GNUNET_free_non_null (n->pending_pong); | ||
508 | n->pending_pong = GNUNET_malloc (sizeof (struct PongMessage)); | ||
509 | memcpy (n->pending_pong, message, sizeof (struct PongMessage)); | ||
510 | return; | ||
511 | } | ||
512 | handle_pong (n, (const struct PongMessage *) message, ats, ats_count); | ||
513 | break; | ||
514 | default: | ||
515 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
516 | _("Unsupported message of type %u received.\n"), | ||
517 | (unsigned int) type); | ||
518 | return; | ||
519 | } | ||
520 | if (n->status == PEER_STATE_KEY_CONFIRMED) | ||
521 | { | ||
522 | now = GNUNET_TIME_absolute_get (); | ||
523 | n->last_activity = now; | ||
524 | changed = GNUNET_YES; | ||
525 | if (!up) | ||
526 | { | ||
527 | GNUNET_STATISTICS_update (stats, gettext_noop ("# established sessions"), | ||
528 | 1, GNUNET_NO); | ||
529 | n->time_established = now; | ||
530 | } | ||
531 | if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) | ||
532 | GNUNET_SCHEDULER_cancel (n->keep_alive_task); | ||
533 | n->keep_alive_task = | ||
534 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide | ||
535 | (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, | ||
536 | 2), &send_keep_alive, n); | ||
537 | } | ||
538 | if (changed) | ||
539 | handle_peer_status_change (n); | ||
540 | } | ||
541 | |||
542 | |||
543 | /** | ||
544 | * Transmit the given message to the given target. | ||
545 | * | ||
546 | * @param target peer that should receive the message (must be connected) | ||
547 | * @param msg message to transmit | ||
548 | * @param timeout by when should the transmission be done? | ||
549 | */ | ||
550 | void | ||
551 | GDS_NEIGHBOURS_transmit (const struct GNUNET_PeerIdentity *target, | ||
552 | const struct GNUNET_MessageHeader *msg, | ||
553 | struct GNUNET_TIME_Relative timeout) | ||
554 | { | ||
555 | |||
556 | } | ||
557 | |||
558 | |||
559 | /** | ||
560 | * Initialize neighbours subsystem. | ||
561 | */ | ||
562 | int | ||
563 | GSC_NEIGHBOURS_init () | ||
564 | { | ||
565 | neighbours = GNUNET_CONTAINER_multihashmap_create (128); | ||
566 | transport = | ||
567 | GNUNET_TRANSPORT_connect (GSC_cfg, | ||
568 | &GSC_my_identity, NULL, | ||
569 | &handle_transport_receive, | ||
570 | &handle_transport_notify_connect, | ||
571 | &handle_transport_notify_disconnect); | ||
572 | if (NULL == transport) | ||
573 | { | ||
574 | GNUNET_CONTAINER_multihashmap_destroy (neighbours); | ||
575 | neighbours = NULL; | ||
576 | return GNUNET_SYSERR; | ||
577 | } | ||
578 | return GNUNET_OK; | ||
579 | } | ||
580 | |||
581 | |||
582 | /** | ||
583 | * Wrapper around 'free_neighbour'. | ||
584 | * | ||
585 | * @param cls unused | ||
586 | * @param key peer identity | ||
587 | * @param value the 'struct Neighbour' to free | ||
588 | * @return GNUNET_OK (continue to iterate) | ||
589 | */ | ||
590 | static int | ||
591 | free_neighbour_helper (void *cls, const GNUNET_HashCode * key, void *value) | ||
592 | { | ||
593 | struct Neighbour *n = value; | ||
594 | |||
595 | free_neighbour (n); | ||
596 | return GNUNET_OK; | ||
597 | } | ||
598 | |||
599 | |||
600 | /** | ||
601 | * Shutdown neighbours subsystem. | ||
602 | */ | ||
603 | void | ||
604 | GSC_NEIGHBOURS_done () | ||
605 | { | ||
606 | if (NULL == transport) | ||
607 | return; | ||
608 | GNUNET_CONTAINER_multihashmap_iterate (neighbours, &free_neighbour_helper, | ||
609 | NULL); | ||
610 | GNUNET_TRANSPORT_disconnect (transport); | ||
611 | transport = NULL; | ||
612 | GNUNET_CONTAINER_multihashmap_destroy (neighbours); | ||
613 | neighbours = NULL; | ||
614 | } | ||
615 | |||
616 | /* end of gnunet-service-core_neighbours.c */ | ||
617 | |||
diff --git a/src/core/gnunet-service-core_plan.c b/src/core/gnunet-service-core_plan.c new file mode 100644 index 000000000..580038e08 --- /dev/null +++ b/src/core/gnunet-service-core_plan.c | |||
@@ -0,0 +1,563 @@ | |||
1 | |||
2 | |||
3 | |||
4 | /** | ||
5 | * Select messages for transmission. This heuristic uses a combination | ||
6 | * of earliest deadline first (EDF) scheduling (with bounded horizon) | ||
7 | * and priority-based discard (in case no feasible schedule exist) and | ||
8 | * speculative optimization (defer any kind of transmission until | ||
9 | * we either create a batch of significant size, 25% of max, or until | ||
10 | * we are close to a deadline). Furthermore, when scheduling the | ||
11 | * heuristic also packs as many messages into the batch as possible, | ||
12 | * starting with those with the earliest deadline. Yes, this is fun. | ||
13 | * | ||
14 | * @param n neighbour to select messages from | ||
15 | * @param size number of bytes to select for transmission | ||
16 | * @param retry_time set to the time when we should try again | ||
17 | * (only valid if this function returns zero) | ||
18 | * @return number of bytes selected, or 0 if we decided to | ||
19 | * defer scheduling overall; in that case, retry_time is set. | ||
20 | */ | ||
21 | static size_t | ||
22 | select_messages (struct Neighbour *n, size_t size, | ||
23 | struct GNUNET_TIME_Relative *retry_time) | ||
24 | { | ||
25 | struct MessageEntry *pos; | ||
26 | struct MessageEntry *min; | ||
27 | struct MessageEntry *last; | ||
28 | unsigned int min_prio; | ||
29 | struct GNUNET_TIME_Absolute t; | ||
30 | struct GNUNET_TIME_Absolute now; | ||
31 | struct GNUNET_TIME_Relative delta; | ||
32 | uint64_t avail; | ||
33 | struct GNUNET_TIME_Relative slack; /* how long could we wait before missing deadlines? */ | ||
34 | size_t off; | ||
35 | uint64_t tsize; | ||
36 | unsigned int queue_size; | ||
37 | int discard_low_prio; | ||
38 | |||
39 | GNUNET_assert (NULL != n->messages); | ||
40 | now = GNUNET_TIME_absolute_get (); | ||
41 | /* last entry in linked list of messages processed */ | ||
42 | last = NULL; | ||
43 | /* should we remove the entry with the lowest | ||
44 | * priority from consideration for scheduling at the | ||
45 | * end of the loop? */ | ||
46 | queue_size = 0; | ||
47 | tsize = 0; | ||
48 | pos = n->messages; | ||
49 | while (pos != NULL) | ||
50 | { | ||
51 | queue_size++; | ||
52 | tsize += pos->size; | ||
53 | pos = pos->next; | ||
54 | } | ||
55 | discard_low_prio = GNUNET_YES; | ||
56 | while (GNUNET_YES == discard_low_prio) | ||
57 | { | ||
58 | min = NULL; | ||
59 | min_prio = UINT_MAX; | ||
60 | discard_low_prio = GNUNET_NO; | ||
61 | /* calculate number of bytes available for transmission at time "t" */ | ||
62 | avail = GNUNET_BANDWIDTH_tracker_get_available (&n->available_send_window); | ||
63 | t = now; | ||
64 | /* how many bytes have we (hypothetically) scheduled so far */ | ||
65 | off = 0; | ||
66 | /* maximum time we can wait before transmitting anything | ||
67 | * and still make all of our deadlines */ | ||
68 | slack = GNUNET_TIME_UNIT_FOREVER_REL; | ||
69 | pos = n->messages; | ||
70 | /* note that we use "*2" here because we want to look | ||
71 | * a bit further into the future; much more makes no | ||
72 | * sense since new message might be scheduled in the | ||
73 | * meantime... */ | ||
74 | while ((pos != NULL) && (off < size * 2)) | ||
75 | { | ||
76 | if (pos->do_transmit == GNUNET_YES) | ||
77 | { | ||
78 | /* already removed from consideration */ | ||
79 | pos = pos->next; | ||
80 | continue; | ||
81 | } | ||
82 | if (discard_low_prio == GNUNET_NO) | ||
83 | { | ||
84 | delta = GNUNET_TIME_absolute_get_difference (t, pos->deadline); | ||
85 | if (delta.rel_value > 0) | ||
86 | { | ||
87 | // FIXME: HUH? Check! | ||
88 | t = pos->deadline; | ||
89 | avail += | ||
90 | GNUNET_BANDWIDTH_value_get_available_until (n->bw_out, delta); | ||
91 | } | ||
92 | if (avail < pos->size) | ||
93 | { | ||
94 | // FIXME: HUH? Check! | ||
95 | discard_low_prio = GNUNET_YES; /* we could not schedule this one! */ | ||
96 | } | ||
97 | else | ||
98 | { | ||
99 | avail -= pos->size; | ||
100 | /* update slack, considering both its absolute deadline | ||
101 | * and relative deadlines caused by other messages | ||
102 | * with their respective load */ | ||
103 | slack = | ||
104 | GNUNET_TIME_relative_min (slack, | ||
105 | GNUNET_BANDWIDTH_value_get_delay_for | ||
106 | (n->bw_out, avail)); | ||
107 | if (pos->deadline.abs_value <= now.abs_value) | ||
108 | { | ||
109 | /* now or never */ | ||
110 | slack = GNUNET_TIME_UNIT_ZERO; | ||
111 | } | ||
112 | else if (GNUNET_YES == pos->got_slack) | ||
113 | { | ||
114 | /* should be soon now! */ | ||
115 | slack = | ||
116 | GNUNET_TIME_relative_min (slack, | ||
117 | GNUNET_TIME_absolute_get_remaining | ||
118 | (pos->slack_deadline)); | ||
119 | } | ||
120 | else | ||
121 | { | ||
122 | slack = | ||
123 | GNUNET_TIME_relative_min (slack, | ||
124 | GNUNET_TIME_absolute_get_difference | ||
125 | (now, pos->deadline)); | ||
126 | pos->got_slack = GNUNET_YES; | ||
127 | pos->slack_deadline = | ||
128 | GNUNET_TIME_absolute_min (pos->deadline, | ||
129 | GNUNET_TIME_relative_to_absolute | ||
130 | (GNUNET_CONSTANTS_MAX_CORK_DELAY)); | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | off += pos->size; | ||
135 | t = GNUNET_TIME_absolute_max (pos->deadline, t); // HUH? Check! | ||
136 | if (pos->priority <= min_prio) | ||
137 | { | ||
138 | /* update min for discard */ | ||
139 | min_prio = pos->priority; | ||
140 | min = pos; | ||
141 | } | ||
142 | pos = pos->next; | ||
143 | } | ||
144 | if (discard_low_prio) | ||
145 | { | ||
146 | GNUNET_assert (min != NULL); | ||
147 | /* remove lowest-priority entry from consideration */ | ||
148 | min->do_transmit = GNUNET_YES; /* means: discard (for now) */ | ||
149 | } | ||
150 | last = pos; | ||
151 | } | ||
152 | /* guard against sending "tiny" messages with large headers without | ||
153 | * urgent deadlines */ | ||
154 | if ((slack.rel_value > GNUNET_CONSTANTS_MAX_CORK_DELAY.rel_value) && | ||
155 | (size > 4 * off) && (queue_size <= MAX_PEER_QUEUE_SIZE - 2)) | ||
156 | { | ||
157 | /* less than 25% of message would be filled with deadlines still | ||
158 | * being met if we delay by one second or more; so just wait for | ||
159 | * more data; but do not wait longer than 1s (since we don't want | ||
160 | * to delay messages for a really long time either). */ | ||
161 | *retry_time = GNUNET_CONSTANTS_MAX_CORK_DELAY; | ||
162 | /* reset do_transmit values for next time */ | ||
163 | while (pos != last) | ||
164 | { | ||
165 | pos->do_transmit = GNUNET_NO; | ||
166 | pos = pos->next; | ||
167 | } | ||
168 | GNUNET_STATISTICS_update (stats, | ||
169 | gettext_noop | ||
170 | ("# transmissions delayed due to corking"), 1, | ||
171 | GNUNET_NO); | ||
172 | #if DEBUG_CORE | ||
173 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
174 | "Deferring transmission for %llums due to underfull message buffer size (%u/%u)\n", | ||
175 | (unsigned long long) retry_time->rel_value, (unsigned int) off, | ||
176 | (unsigned int) size); | ||
177 | #endif | ||
178 | return 0; | ||
179 | } | ||
180 | /* select marked messages (up to size) for transmission */ | ||
181 | off = 0; | ||
182 | pos = n->messages; | ||
183 | while (pos != last) | ||
184 | { | ||
185 | if ((pos->size <= size) && (pos->do_transmit == GNUNET_NO)) | ||
186 | { | ||
187 | pos->do_transmit = GNUNET_YES; /* mark for transmission */ | ||
188 | off += pos->size; | ||
189 | size -= pos->size; | ||
190 | #if DEBUG_CORE > 1 | ||
191 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
192 | "Selecting message of size %u for transmission\n", | ||
193 | (unsigned int) pos->size); | ||
194 | #endif | ||
195 | } | ||
196 | else | ||
197 | { | ||
198 | #if DEBUG_CORE > 1 | ||
199 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
200 | "Not selecting message of size %u for transmission at this time (maximum is %u)\n", | ||
201 | (unsigned int) pos->size, size); | ||
202 | #endif | ||
203 | pos->do_transmit = GNUNET_NO; /* mark for not transmitting! */ | ||
204 | } | ||
205 | pos = pos->next; | ||
206 | } | ||
207 | #if DEBUG_CORE | ||
208 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
209 | "Selected %llu/%llu bytes of %u/%u plaintext messages for transmission to `%4s'.\n", | ||
210 | (unsigned long long) off, (unsigned long long) tsize, queue_size, | ||
211 | (unsigned int) MAX_PEER_QUEUE_SIZE, GNUNET_i2s (&n->peer)); | ||
212 | #endif | ||
213 | return off; | ||
214 | } | ||
215 | |||
216 | |||
217 | /** | ||
218 | * Batch multiple messages into a larger buffer. | ||
219 | * | ||
220 | * @param n neighbour to take messages from | ||
221 | * @param buf target buffer | ||
222 | * @param size size of buf | ||
223 | * @param deadline set to transmission deadline for the result | ||
224 | * @param retry_time set to the time when we should try again | ||
225 | * (only valid if this function returns zero) | ||
226 | * @param priority set to the priority of the batch | ||
227 | * @return number of bytes written to buf (can be zero) | ||
228 | */ | ||
229 | static size_t | ||
230 | batch_message (struct Neighbour *n, char *buf, size_t size, | ||
231 | struct GNUNET_TIME_Absolute *deadline, | ||
232 | struct GNUNET_TIME_Relative *retry_time, unsigned int *priority) | ||
233 | { | ||
234 | char ntmb[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; | ||
235 | struct NotifyTrafficMessage *ntm = (struct NotifyTrafficMessage *) ntmb; | ||
236 | struct MessageEntry *pos; | ||
237 | struct MessageEntry *prev; | ||
238 | struct MessageEntry *next; | ||
239 | size_t ret; | ||
240 | |||
241 | ret = 0; | ||
242 | *priority = 0; | ||
243 | *deadline = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
244 | *retry_time = GNUNET_TIME_UNIT_FOREVER_REL; | ||
245 | if (0 == select_messages (n, size, retry_time)) | ||
246 | { | ||
247 | #if DEBUG_CORE | ||
248 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
249 | "No messages selected, will try again in %llu ms\n", | ||
250 | retry_time->rel_value); | ||
251 | #endif | ||
252 | return 0; | ||
253 | } | ||
254 | ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND); | ||
255 | ntm->ats_count = htonl (0); | ||
256 | ntm->ats.type = htonl (0); | ||
257 | ntm->ats.value = htonl (0); | ||
258 | ntm->peer = n->peer; | ||
259 | pos = n->messages; | ||
260 | prev = NULL; | ||
261 | while ((pos != NULL) && (size >= sizeof (struct GNUNET_MessageHeader))) | ||
262 | { | ||
263 | next = pos->next; | ||
264 | if (GNUNET_YES == pos->do_transmit) | ||
265 | { | ||
266 | GNUNET_assert (pos->size <= size); | ||
267 | /* do notifications */ | ||
268 | /* FIXME: track if we have *any* client that wants | ||
269 | * full notifications and only do this if that is | ||
270 | * actually true */ | ||
271 | if (pos->size < | ||
272 | GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct NotifyTrafficMessage)) | ||
273 | { | ||
274 | memcpy (&ntm[1], &pos[1], pos->size); | ||
275 | ntm->header.size = | ||
276 | htons (sizeof (struct NotifyTrafficMessage) + | ||
277 | sizeof (struct GNUNET_MessageHeader)); | ||
278 | send_to_all_clients (&ntm->header, GNUNET_YES, | ||
279 | GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND); | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | /* message too large for 'full' notifications, we do at | ||
284 | * least the 'hdr' type */ | ||
285 | memcpy (&ntm[1], &pos[1], sizeof (struct GNUNET_MessageHeader)); | ||
286 | } | ||
287 | ntm->header.size = | ||
288 | htons (sizeof (struct NotifyTrafficMessage) + pos->size); | ||
289 | send_to_all_clients (&ntm->header, GNUNET_YES, | ||
290 | GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND); | ||
291 | #if DEBUG_HANDSHAKE | ||
292 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
293 | "Encrypting %u bytes with message of type %u and size %u\n", | ||
294 | pos->size, | ||
295 | (unsigned int) | ||
296 | ntohs (((const struct GNUNET_MessageHeader *) &pos[1])->type), | ||
297 | (unsigned int) | ||
298 | ntohs (((const struct GNUNET_MessageHeader *) | ||
299 | &pos[1])->size)); | ||
300 | #endif | ||
301 | /* copy for encrypted transmission */ | ||
302 | memcpy (&buf[ret], &pos[1], pos->size); | ||
303 | ret += pos->size; | ||
304 | size -= pos->size; | ||
305 | *priority += pos->priority; | ||
306 | #if DEBUG_CORE > 1 | ||
307 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
308 | "Adding plaintext message of size %u with deadline %llu ms to batch\n", | ||
309 | (unsigned int) pos->size, | ||
310 | (unsigned long long) | ||
311 | GNUNET_TIME_absolute_get_remaining (pos->deadline).rel_value); | ||
312 | #endif | ||
313 | deadline->abs_value = | ||
314 | GNUNET_MIN (deadline->abs_value, pos->deadline.abs_value); | ||
315 | GNUNET_free (pos); | ||
316 | if (prev == NULL) | ||
317 | n->messages = next; | ||
318 | else | ||
319 | prev->next = next; | ||
320 | } | ||
321 | else | ||
322 | { | ||
323 | prev = pos; | ||
324 | } | ||
325 | pos = next; | ||
326 | } | ||
327 | #if DEBUG_CORE > 1 | ||
328 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
329 | "Deadline for message batch is %llu ms\n", | ||
330 | GNUNET_TIME_absolute_get_remaining (*deadline).rel_value); | ||
331 | #endif | ||
332 | return ret; | ||
333 | } | ||
334 | |||
335 | |||
336 | /** | ||
337 | * Remove messages with deadlines that have long expired from | ||
338 | * the queue. | ||
339 | * | ||
340 | * @param n neighbour to inspect | ||
341 | */ | ||
342 | static void | ||
343 | discard_expired_messages (struct Neighbour *n) | ||
344 | { | ||
345 | struct MessageEntry *prev; | ||
346 | struct MessageEntry *next; | ||
347 | struct MessageEntry *pos; | ||
348 | struct GNUNET_TIME_Absolute now; | ||
349 | struct GNUNET_TIME_Relative delta; | ||
350 | int disc; | ||
351 | unsigned int queue_length; | ||
352 | |||
353 | disc = GNUNET_NO; | ||
354 | now = GNUNET_TIME_absolute_get (); | ||
355 | prev = NULL; | ||
356 | queue_length = 0; | ||
357 | pos = n->messages; | ||
358 | while (pos != NULL) | ||
359 | { | ||
360 | queue_length++; | ||
361 | next = pos->next; | ||
362 | delta = GNUNET_TIME_absolute_get_difference (pos->deadline, now); | ||
363 | if (delta.rel_value > PAST_EXPIRATION_DISCARD_TIME.rel_value) | ||
364 | { | ||
365 | #if DEBUG_CORE | ||
366 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
367 | "Message is %llu ms past due, discarding.\n", | ||
368 | delta.rel_value); | ||
369 | #endif | ||
370 | if (prev == NULL) | ||
371 | n->messages = next; | ||
372 | else | ||
373 | prev->next = next; | ||
374 | GNUNET_STATISTICS_update (stats, | ||
375 | gettext_noop | ||
376 | ("# messages discarded (expired prior to transmission)"), | ||
377 | 1, GNUNET_NO); | ||
378 | disc = GNUNET_YES; | ||
379 | GNUNET_free (pos); | ||
380 | } | ||
381 | else | ||
382 | prev = pos; | ||
383 | pos = next; | ||
384 | } | ||
385 | if ( (GNUNET_YES == disc) && | ||
386 | (queue_length == MAX_PEER_QUEUE_SIZE) ) | ||
387 | schedule_peer_messages (n); | ||
388 | } | ||
389 | |||
390 | |||
391 | /** | ||
392 | * Signature of the main function of a task. | ||
393 | * | ||
394 | * @param cls closure | ||
395 | * @param tc context information (why was this task triggered now) | ||
396 | */ | ||
397 | static void | ||
398 | retry_plaintext_processing (void *cls, | ||
399 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
400 | { | ||
401 | struct Neighbour *n = cls; | ||
402 | |||
403 | n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK; | ||
404 | process_plaintext_neighbour_queue (n); | ||
405 | } | ||
406 | |||
407 | |||
408 | /** | ||
409 | * Check if we have plaintext messages for the specified neighbour | ||
410 | * pending, and if so, consider batching and encrypting them (and | ||
411 | * then trigger processing of the encrypted queue if needed). | ||
412 | * | ||
413 | * @param n neighbour to check. | ||
414 | */ | ||
415 | static void | ||
416 | process_plaintext_neighbour_queue (struct Neighbour *n) | ||
417 | { | ||
418 | char pbuf[GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE + sizeof (struct EncryptedMessage)]; /* plaintext */ | ||
419 | size_t used; | ||
420 | struct EncryptedMessage *em; /* encrypted message */ | ||
421 | struct EncryptedMessage *ph; /* plaintext header */ | ||
422 | struct MessageEntry *me; | ||
423 | unsigned int priority; | ||
424 | struct GNUNET_TIME_Absolute deadline; | ||
425 | struct GNUNET_TIME_Relative retry_time; | ||
426 | struct GNUNET_CRYPTO_AesInitializationVector iv; | ||
427 | struct GNUNET_CRYPTO_AuthKey auth_key; | ||
428 | |||
429 | if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_TASK) | ||
430 | { | ||
431 | GNUNET_SCHEDULER_cancel (n->retry_plaintext_task); | ||
432 | n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK; | ||
433 | } | ||
434 | switch (n->status) | ||
435 | { | ||
436 | case PEER_STATE_DOWN: | ||
437 | send_key (n); | ||
438 | #if DEBUG_CORE | ||
439 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
440 | "Not yet connected to `%4s', deferring processing of plaintext messages.\n", | ||
441 | GNUNET_i2s (&n->peer)); | ||
442 | #endif | ||
443 | return; | ||
444 | case PEER_STATE_KEY_SENT: | ||
445 | if (n->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK) | ||
446 | n->retry_set_key_task = | ||
447 | GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency, | ||
448 | &set_key_retry_task, n); | ||
449 | #if DEBUG_CORE | ||
450 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
451 | "Not yet connected to `%4s', deferring processing of plaintext messages.\n", | ||
452 | GNUNET_i2s (&n->peer)); | ||
453 | #endif | ||
454 | return; | ||
455 | case PEER_STATE_KEY_RECEIVED: | ||
456 | if (n->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK) | ||
457 | n->retry_set_key_task = | ||
458 | GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency, | ||
459 | &set_key_retry_task, n); | ||
460 | #if DEBUG_CORE | ||
461 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
462 | "Not yet connected to `%4s', deferring processing of plaintext messages.\n", | ||
463 | GNUNET_i2s (&n->peer)); | ||
464 | #endif | ||
465 | return; | ||
466 | case PEER_STATE_KEY_CONFIRMED: | ||
467 | /* ready to continue */ | ||
468 | break; | ||
469 | } | ||
470 | discard_expired_messages (n); | ||
471 | if (n->messages == NULL) | ||
472 | { | ||
473 | #if DEBUG_CORE | ||
474 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
475 | "Plaintext message queue for `%4s' is empty.\n", | ||
476 | GNUNET_i2s (&n->peer)); | ||
477 | #endif | ||
478 | return; /* no pending messages */ | ||
479 | } | ||
480 | if (n->encrypted_head != NULL) | ||
481 | { | ||
482 | #if DEBUG_CORE > 2 | ||
483 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
484 | "Encrypted message queue for `%4s' is still full, delaying plaintext processing.\n", | ||
485 | GNUNET_i2s (&n->peer)); | ||
486 | #endif | ||
487 | return; /* wait for messages already encrypted to be | ||
488 | * processed first! */ | ||
489 | } | ||
490 | ph = (struct EncryptedMessage *) pbuf; | ||
491 | deadline = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
492 | priority = 0; | ||
493 | used = sizeof (struct EncryptedMessage); | ||
494 | used += | ||
495 | batch_message (n, &pbuf[used], | ||
496 | GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE, &deadline, | ||
497 | &retry_time, &priority); | ||
498 | if (used == sizeof (struct EncryptedMessage)) | ||
499 | { | ||
500 | #if DEBUG_CORE > 1 | ||
501 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
502 | "No messages selected for transmission to `%4s' at this time, will try again later.\n", | ||
503 | GNUNET_i2s (&n->peer)); | ||
504 | #endif | ||
505 | /* no messages selected for sending, try again later... */ | ||
506 | n->retry_plaintext_task = | ||
507 | GNUNET_SCHEDULER_add_delayed (retry_time, &retry_plaintext_processing, | ||
508 | n); | ||
509 | return; | ||
510 | } | ||
511 | #if DEBUG_CORE_QUOTA | ||
512 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
513 | "Sending %u b/s as new limit to peer `%4s'\n", | ||
514 | (unsigned int) ntohl (n->bw_in.value__), GNUNET_i2s (&n->peer)); | ||
515 | #endif | ||
516 | ph->iv_seed = | ||
517 | htonl (GNUNET_CRYPTO_random_u32 | ||
518 | (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX)); | ||
519 | ph->sequence_number = htonl (++n->last_sequence_number_sent); | ||
520 | ph->inbound_bw_limit = n->bw_in; | ||
521 | ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); | ||
522 | |||
523 | /* setup encryption message header */ | ||
524 | me = GNUNET_malloc (sizeof (struct MessageEntry) + used); | ||
525 | me->deadline = deadline; | ||
526 | me->priority = priority; | ||
527 | me->size = used; | ||
528 | em = (struct EncryptedMessage *) &me[1]; | ||
529 | em->header.size = htons (used); | ||
530 | em->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE); | ||
531 | em->iv_seed = ph->iv_seed; | ||
532 | derive_iv (&iv, &n->encrypt_key, ph->iv_seed, &n->peer); | ||
533 | /* encrypt */ | ||
534 | #if DEBUG_HANDSHAKE | ||
535 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
536 | "Encrypting %u bytes of plaintext messages for `%4s' for transmission in %llums.\n", | ||
537 | (unsigned int) used - ENCRYPTED_HEADER_SIZE, | ||
538 | GNUNET_i2s (&n->peer), | ||
539 | (unsigned long long) | ||
540 | GNUNET_TIME_absolute_get_remaining (deadline).rel_value); | ||
541 | #endif | ||
542 | GNUNET_assert (GNUNET_OK == | ||
543 | do_encrypt (n, &iv, &ph->sequence_number, &em->sequence_number, | ||
544 | used - ENCRYPTED_HEADER_SIZE)); | ||
545 | derive_auth_key (&auth_key, &n->encrypt_key, ph->iv_seed, | ||
546 | n->encrypt_key_created); | ||
547 | GNUNET_CRYPTO_hmac (&auth_key, &em->sequence_number, | ||
548 | used - ENCRYPTED_HEADER_SIZE, &em->hmac); | ||
549 | #if DEBUG_HANDSHAKE | ||
550 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
551 | "Authenticated %u bytes of ciphertext %u: `%s'\n", | ||
552 | used - ENCRYPTED_HEADER_SIZE, | ||
553 | GNUNET_CRYPTO_crc32_n (&em->sequence_number, | ||
554 | used - ENCRYPTED_HEADER_SIZE), | ||
555 | GNUNET_h2s (&em->hmac)); | ||
556 | #endif | ||
557 | /* append to transmission list */ | ||
558 | GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head, n->encrypted_tail, | ||
559 | n->encrypted_tail, me); | ||
560 | process_encrypted_neighbour_queue (n); | ||
561 | schedule_peer_messages (n); | ||
562 | } | ||
563 | |||
diff --git a/src/core/gnunet-service-core_sessions.c b/src/core/gnunet-service-core_sessions.c new file mode 100644 index 000000000..13593e9d6 --- /dev/null +++ b/src/core/gnunet-service-core_sessions.c | |||
@@ -0,0 +1,713 @@ | |||
1 | /* code for managing of 'encrypted' sessions (key exchange done) */ | ||
2 | |||
3 | |||
4 | /** | ||
5 | * Record kept for each request for transmission issued by a | ||
6 | * client that is still pending. | ||
7 | */ | ||
8 | struct ClientActiveRequest; | ||
9 | |||
10 | /** | ||
11 | * Data kept per session. | ||
12 | */ | ||
13 | struct Session | ||
14 | { | ||
15 | /** | ||
16 | * Identity of the other peer. | ||
17 | */ | ||
18 | struct GNUNET_PeerIdentity peer; | ||
19 | |||
20 | /** | ||
21 | * Head of list of requests from clients for transmission to | ||
22 | * this peer. | ||
23 | */ | ||
24 | struct ClientActiveRequest *active_client_request_head; | ||
25 | |||
26 | /** | ||
27 | * Tail of list of requests from clients for transmission to | ||
28 | * this peer. | ||
29 | */ | ||
30 | struct ClientActiveRequest *active_client_request_tail; | ||
31 | |||
32 | /** | ||
33 | * Performance data for the peer. | ||
34 | */ | ||
35 | struct GNUNET_TRANSPORT_ATS_Information *ats; | ||
36 | |||
37 | /** | ||
38 | * Information about the key exchange with the other peer. | ||
39 | */ | ||
40 | struct GSC_KeyExchangeInfo *kxinfo; | ||
41 | |||
42 | /** | ||
43 | * ID of task used for sending keep-alive pings. | ||
44 | */ | ||
45 | GNUNET_SCHEDULER_TaskIdentifier keep_alive_task; | ||
46 | |||
47 | /** | ||
48 | * ID of task used for cleaning up dead neighbour entries. | ||
49 | */ | ||
50 | GNUNET_SCHEDULER_TaskIdentifier dead_clean_task; | ||
51 | |||
52 | /** | ||
53 | * ID of task used for updating bandwidth quota for this neighbour. | ||
54 | */ | ||
55 | GNUNET_SCHEDULER_TaskIdentifier quota_update_task; | ||
56 | |||
57 | /** | ||
58 | * At what time did we initially establish (as in, complete session | ||
59 | * key handshake) this connection? Should be zero if status != KEY_CONFIRMED. | ||
60 | */ | ||
61 | struct GNUNET_TIME_Absolute time_established; | ||
62 | |||
63 | /** | ||
64 | * At what time did we last receive an encrypted message from the | ||
65 | * other peer? Should be zero if status != KEY_CONFIRMED. | ||
66 | */ | ||
67 | struct GNUNET_TIME_Absolute last_activity; | ||
68 | |||
69 | /** | ||
70 | * Tracking bandwidth for sending to this peer. | ||
71 | */ | ||
72 | struct GNUNET_BANDWIDTH_Tracker available_send_window; | ||
73 | |||
74 | /** | ||
75 | * Tracking bandwidth for receiving from this peer. | ||
76 | */ | ||
77 | struct GNUNET_BANDWIDTH_Tracker available_recv_window; | ||
78 | |||
79 | /** | ||
80 | * How valueable were the messages of this peer recently? | ||
81 | */ | ||
82 | unsigned long long current_preference; | ||
83 | |||
84 | /** | ||
85 | * Number of entries in 'ats'. | ||
86 | */ | ||
87 | unsigned int ats_count; | ||
88 | |||
89 | /** | ||
90 | * Bit map indicating which of the 32 sequence numbers before the last | ||
91 | * were received (good for accepting out-of-order packets and | ||
92 | * estimating reliability of the connection) | ||
93 | */ | ||
94 | unsigned int last_packets_bitmap; | ||
95 | |||
96 | /** | ||
97 | * last sequence number received on this connection (highest) | ||
98 | */ | ||
99 | uint32_t last_sequence_number_received; | ||
100 | |||
101 | /** | ||
102 | * last sequence number transmitted | ||
103 | */ | ||
104 | uint32_t last_sequence_number_sent; | ||
105 | |||
106 | /** | ||
107 | * Available bandwidth in for this peer (current target). | ||
108 | */ | ||
109 | struct GNUNET_BANDWIDTH_Value32NBO bw_in; | ||
110 | |||
111 | /** | ||
112 | * Available bandwidth out for this peer (current target). | ||
113 | */ | ||
114 | struct GNUNET_BANDWIDTH_Value32NBO bw_out; | ||
115 | |||
116 | /** | ||
117 | * Internal bandwidth limit set for this peer (initially typically | ||
118 | * set to "-1"). Actual "bw_out" is MIN of | ||
119 | * "bpm_out_internal_limit" and "bw_out_external_limit". | ||
120 | */ | ||
121 | struct GNUNET_BANDWIDTH_Value32NBO bw_out_internal_limit; | ||
122 | |||
123 | /** | ||
124 | * External bandwidth limit set for this peer by the | ||
125 | * peer that we are communicating with. "bw_out" is MIN of | ||
126 | * "bw_out_internal_limit" and "bw_out_external_limit". | ||
127 | */ | ||
128 | struct GNUNET_BANDWIDTH_Value32NBO bw_out_external_limit; | ||
129 | |||
130 | }; | ||
131 | |||
132 | |||
133 | /** | ||
134 | * Map of peer identities to 'struct Session'. | ||
135 | */ | ||
136 | static struct GNUNET_CONTAINER_MultiHashMap *sessions; | ||
137 | |||
138 | |||
139 | /** | ||
140 | * Session entry for "this" peer. | ||
141 | */ | ||
142 | static struct Session self; | ||
143 | |||
144 | /** | ||
145 | * Sum of all preferences among all neighbours. | ||
146 | */ | ||
147 | static unsigned long long preference_sum; | ||
148 | |||
149 | |||
150 | // FIXME......... | ||
151 | |||
152 | /** | ||
153 | * At what time should the connection to the given neighbour | ||
154 | * time out (given no further activity?) | ||
155 | * | ||
156 | * @param n neighbour in question | ||
157 | * @return absolute timeout | ||
158 | */ | ||
159 | static struct GNUNET_TIME_Absolute | ||
160 | get_neighbour_timeout (struct Neighbour *n) | ||
161 | { | ||
162 | return GNUNET_TIME_absolute_add (n->last_activity, | ||
163 | GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); | ||
164 | } | ||
165 | |||
166 | |||
167 | /** | ||
168 | * Helper function for update_preference_sum. | ||
169 | */ | ||
170 | static int | ||
171 | update_preference (void *cls, const GNUNET_HashCode * key, void *value) | ||
172 | { | ||
173 | unsigned long long *ps = cls; | ||
174 | struct Neighbour *n = value; | ||
175 | |||
176 | n->current_preference /= 2; | ||
177 | *ps += n->current_preference; | ||
178 | return GNUNET_OK; | ||
179 | } | ||
180 | |||
181 | |||
182 | /** | ||
183 | * A preference value for a neighbour was update. Update | ||
184 | * the preference sum accordingly. | ||
185 | * | ||
186 | * @param inc how much was a preference value increased? | ||
187 | */ | ||
188 | static void | ||
189 | update_preference_sum (unsigned long long inc) | ||
190 | { | ||
191 | unsigned long long os; | ||
192 | |||
193 | os = preference_sum; | ||
194 | preference_sum += inc; | ||
195 | if (preference_sum >= os) | ||
196 | return; /* done! */ | ||
197 | /* overflow! compensate by cutting all values in half! */ | ||
198 | preference_sum = 0; | ||
199 | GNUNET_CONTAINER_multihashmap_iterate (neighbours, &update_preference, | ||
200 | &preference_sum); | ||
201 | GNUNET_STATISTICS_set (stats, gettext_noop ("# total peer preference"), | ||
202 | preference_sum, GNUNET_NO); | ||
203 | } | ||
204 | |||
205 | |||
206 | /** | ||
207 | * Find the entry for the given neighbour. | ||
208 | * | ||
209 | * @param peer identity of the neighbour | ||
210 | * @return NULL if we are not connected, otherwise the | ||
211 | * neighbour's entry. | ||
212 | */ | ||
213 | static struct Neighbour * | ||
214 | find_neighbour (const struct GNUNET_PeerIdentity *peer) | ||
215 | { | ||
216 | return GNUNET_CONTAINER_multihashmap_get (neighbours, &peer->hashPubKey); | ||
217 | } | ||
218 | |||
219 | |||
220 | /** | ||
221 | * Function called by transport telling us that a peer | ||
222 | * changed status. | ||
223 | * | ||
224 | * @param n the peer that changed status | ||
225 | */ | ||
226 | static void | ||
227 | handle_peer_status_change (struct Neighbour *n) | ||
228 | { | ||
229 | struct PeerStatusNotifyMessage *psnm; | ||
230 | char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; | ||
231 | struct GNUNET_TRANSPORT_ATS_Information *ats; | ||
232 | size_t size; | ||
233 | |||
234 | if ((!n->is_connected) || (n->status != PEER_STATE_KEY_CONFIRMED)) | ||
235 | return; | ||
236 | #if DEBUG_CORE > 1 | ||
237 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' changed status\n", | ||
238 | GNUNET_i2s (&n->peer)); | ||
239 | #endif | ||
240 | size = | ||
241 | sizeof (struct PeerStatusNotifyMessage) + | ||
242 | n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
243 | if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) | ||
244 | { | ||
245 | GNUNET_break (0); | ||
246 | /* recovery strategy: throw away performance data */ | ||
247 | GNUNET_array_grow (n->ats, n->ats_count, 0); | ||
248 | size = | ||
249 | sizeof (struct PeerStatusNotifyMessage) + | ||
250 | n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information); | ||
251 | } | ||
252 | psnm = (struct PeerStatusNotifyMessage *) buf; | ||
253 | psnm->header.size = htons (size); | ||
254 | psnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_STATUS_CHANGE); | ||
255 | psnm->timeout = GNUNET_TIME_absolute_hton (get_neighbour_timeout (n)); | ||
256 | psnm->bandwidth_in = n->bw_in; | ||
257 | psnm->bandwidth_out = n->bw_out; | ||
258 | psnm->peer = n->peer; | ||
259 | psnm->ats_count = htonl (n->ats_count); | ||
260 | ats = &psnm->ats; | ||
261 | memcpy (ats, n->ats, | ||
262 | n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information)); | ||
263 | ats[n->ats_count].type = htonl (0); | ||
264 | ats[n->ats_count].value = htonl (0); | ||
265 | send_to_all_clients (&psnm->header, GNUNET_YES, | ||
266 | GNUNET_CORE_OPTION_SEND_STATUS_CHANGE); | ||
267 | GNUNET_STATISTICS_update (stats, gettext_noop ("# peer status changes"), 1, | ||
268 | GNUNET_NO); | ||
269 | } | ||
270 | |||
271 | |||
272 | |||
273 | /** | ||
274 | * Go over our message queue and if it is not too long, go | ||
275 | * over the pending requests from clients for this | ||
276 | * neighbour and send some clients a 'READY' notification. | ||
277 | * | ||
278 | * @param n which peer to process | ||
279 | */ | ||
280 | static void | ||
281 | schedule_peer_messages (struct Neighbour *n) | ||
282 | { | ||
283 | struct SendMessageReady smr; | ||
284 | struct ClientActiveRequest *car; | ||
285 | struct ClientActiveRequest *pos; | ||
286 | struct Client *c; | ||
287 | struct MessageEntry *mqe; | ||
288 | unsigned int queue_size; | ||
289 | |||
290 | /* check if neighbour queue is empty enough! */ | ||
291 | if (n != &self) | ||
292 | { | ||
293 | queue_size = 0; | ||
294 | mqe = n->messages; | ||
295 | while (mqe != NULL) | ||
296 | { | ||
297 | queue_size++; | ||
298 | mqe = mqe->next; | ||
299 | } | ||
300 | if (queue_size >= MAX_PEER_QUEUE_SIZE) | ||
301 | { | ||
302 | #if DEBUG_CORE_CLIENT | ||
303 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
304 | "Not considering client transmission requests: queue full\n"); | ||
305 | #endif | ||
306 | return; /* queue still full */ | ||
307 | } | ||
308 | /* find highest priority request */ | ||
309 | pos = n->active_client_request_head; | ||
310 | car = NULL; | ||
311 | while (pos != NULL) | ||
312 | { | ||
313 | if ((car == NULL) || (pos->priority > car->priority)) | ||
314 | car = pos; | ||
315 | pos = pos->next; | ||
316 | } | ||
317 | } | ||
318 | else | ||
319 | { | ||
320 | car = n->active_client_request_head; | ||
321 | } | ||
322 | if (car == NULL) | ||
323 | return; /* no pending requests */ | ||
324 | #if DEBUG_CORE_CLIENT | ||
325 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
326 | "Permitting client transmission request to `%s'\n", | ||
327 | GNUNET_i2s (&n->peer)); | ||
328 | #endif | ||
329 | c = car->client; | ||
330 | GNUNET_CONTAINER_DLL_remove (n->active_client_request_head, | ||
331 | n->active_client_request_tail, car); | ||
332 | GNUNET_assert (GNUNET_YES == | ||
333 | GNUNET_CONTAINER_multihashmap_remove (c->requests, | ||
334 | &n->peer.hashPubKey, | ||
335 | car)); | ||
336 | smr.header.size = htons (sizeof (struct SendMessageReady)); | ||
337 | smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY); | ||
338 | smr.size = htons (car->msize); | ||
339 | smr.smr_id = car->smr_id; | ||
340 | smr.peer = n->peer; | ||
341 | send_to_client (c, &smr.header, GNUNET_NO); | ||
342 | GNUNET_free (car); | ||
343 | } | ||
344 | |||
345 | |||
346 | |||
347 | /** | ||
348 | * Free the given entry for the neighbour (it has | ||
349 | * already been removed from the list at this point). | ||
350 | * | ||
351 | * @param n neighbour to free | ||
352 | */ | ||
353 | static void | ||
354 | free_neighbour (struct Neighbour *n) | ||
355 | { | ||
356 | struct MessageEntry *m; | ||
357 | struct ClientActiveRequest *car; | ||
358 | |||
359 | #if DEBUG_CORE | ||
360 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
361 | "Destroying neighbour entry for peer `%4s'\n", | ||
362 | GNUNET_i2s (&n->peer)); | ||
363 | #endif | ||
364 | if (n->skm != NULL) | ||
365 | { | ||
366 | GNUNET_free (n->skm); | ||
367 | n->skm = NULL; | ||
368 | } | ||
369 | while (NULL != (m = n->messages)) | ||
370 | { | ||
371 | n->messages = m->next; | ||
372 | GNUNET_free (m); | ||
373 | } | ||
374 | while (NULL != (m = n->encrypted_head)) | ||
375 | { | ||
376 | GNUNET_CONTAINER_DLL_remove (n->encrypted_head, n->encrypted_tail, m); | ||
377 | GNUNET_free (m); | ||
378 | } | ||
379 | while (NULL != (car = n->active_client_request_head)) | ||
380 | { | ||
381 | GNUNET_CONTAINER_DLL_remove (n->active_client_request_head, | ||
382 | n->active_client_request_tail, car); | ||
383 | GNUNET_assert (GNUNET_YES == | ||
384 | GNUNET_CONTAINER_multihashmap_remove (car->client->requests, | ||
385 | &n->peer.hashPubKey, | ||
386 | car)); | ||
387 | GNUNET_free (car); | ||
388 | } | ||
389 | if (NULL != n->th) | ||
390 | { | ||
391 | GNUNET_TRANSPORT_notify_transmit_ready_cancel (n->th); | ||
392 | n->th = NULL; | ||
393 | } | ||
394 | if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_TASK) | ||
395 | GNUNET_SCHEDULER_cancel (n->retry_plaintext_task); | ||
396 | if (n->quota_update_task != GNUNET_SCHEDULER_NO_TASK) | ||
397 | GNUNET_SCHEDULER_cancel (n->quota_update_task); | ||
398 | if (n->dead_clean_task != GNUNET_SCHEDULER_NO_TASK) | ||
399 | GNUNET_SCHEDULER_cancel (n->dead_clean_task); | ||
400 | if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) | ||
401 | GNUNET_SCHEDULER_cancel (n->keep_alive_task); | ||
402 | if (n->status == PEER_STATE_KEY_CONFIRMED) | ||
403 | GNUNET_STATISTICS_update (stats, gettext_noop ("# established sessions"), | ||
404 | -1, GNUNET_NO); | ||
405 | GNUNET_array_grow (n->ats, n->ats_count, 0); | ||
406 | GNUNET_free_non_null (n->pending_ping); | ||
407 | GNUNET_free_non_null (n->pending_pong); | ||
408 | GNUNET_free (n); | ||
409 | } | ||
410 | |||
411 | |||
412 | |||
413 | /** | ||
414 | * Task triggered when a neighbour entry is about to time out | ||
415 | * (and we should prevent this by sending a PING). | ||
416 | * | ||
417 | * @param cls the 'struct Neighbour' | ||
418 | * @param tc scheduler context (not used) | ||
419 | */ | ||
420 | static void | ||
421 | send_keep_alive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
422 | { | ||
423 | struct Neighbour *n = cls; | ||
424 | struct GNUNET_TIME_Relative retry; | ||
425 | struct GNUNET_TIME_Relative left; | ||
426 | struct MessageEntry *me; | ||
427 | struct PingMessage pp; | ||
428 | struct PingMessage *pm; | ||
429 | struct GNUNET_CRYPTO_AesInitializationVector iv; | ||
430 | |||
431 | n->keep_alive_task = GNUNET_SCHEDULER_NO_TASK; | ||
432 | /* send PING */ | ||
433 | me = GNUNET_malloc (sizeof (struct MessageEntry) + | ||
434 | sizeof (struct PingMessage)); | ||
435 | me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PING_DELAY); | ||
436 | me->priority = PING_PRIORITY; | ||
437 | me->size = sizeof (struct PingMessage); | ||
438 | GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head, n->encrypted_tail, | ||
439 | n->encrypted_tail, me); | ||
440 | pm = (struct PingMessage *) &me[1]; | ||
441 | pm->header.size = htons (sizeof (struct PingMessage)); | ||
442 | pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING); | ||
443 | pm->iv_seed = | ||
444 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); | ||
445 | derive_iv (&iv, &n->encrypt_key, pm->iv_seed, &n->peer); | ||
446 | pp.challenge = n->ping_challenge; | ||
447 | pp.target = n->peer; | ||
448 | #if DEBUG_HANDSHAKE | ||
449 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
450 | "Encrypting `%s' message with challenge %u for `%4s' using key %u, IV %u (salt %u).\n", | ||
451 | "PING", (unsigned int) n->ping_challenge, GNUNET_i2s (&n->peer), | ||
452 | (unsigned int) n->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, | ||
453 | sizeof | ||
454 | (iv)), | ||
455 | pm->iv_seed); | ||
456 | #endif | ||
457 | do_encrypt (n, &iv, &pp.target, &pm->target, | ||
458 | sizeof (struct PingMessage) - ((void *) &pm->target - | ||
459 | (void *) pm)); | ||
460 | process_encrypted_neighbour_queue (n); | ||
461 | /* reschedule PING job */ | ||
462 | left = GNUNET_TIME_absolute_get_remaining (get_neighbour_timeout (n)); | ||
463 | retry = | ||
464 | GNUNET_TIME_relative_max (GNUNET_TIME_relative_divide (left, 2), | ||
465 | MIN_PING_FREQUENCY); | ||
466 | n->keep_alive_task = | ||
467 | GNUNET_SCHEDULER_add_delayed (retry, &send_keep_alive, n); | ||
468 | |||
469 | } | ||
470 | |||
471 | /** | ||
472 | * Consider freeing the given neighbour since we may not need | ||
473 | * to keep it around anymore. | ||
474 | * | ||
475 | * @param n neighbour to consider discarding | ||
476 | */ | ||
477 | static void | ||
478 | consider_free_neighbour (struct Neighbour *n); | ||
479 | |||
480 | |||
481 | /** | ||
482 | * Task triggered when a neighbour entry might have gotten stale. | ||
483 | * | ||
484 | * @param cls the 'struct Neighbour' | ||
485 | * @param tc scheduler context (not used) | ||
486 | */ | ||
487 | static void | ||
488 | consider_free_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
489 | { | ||
490 | struct Neighbour *n = cls; | ||
491 | |||
492 | n->dead_clean_task = GNUNET_SCHEDULER_NO_TASK; | ||
493 | consider_free_neighbour (n); | ||
494 | } | ||
495 | |||
496 | |||
497 | /** | ||
498 | * Consider freeing the given neighbour since we may not need | ||
499 | * to keep it around anymore. | ||
500 | * | ||
501 | * @param n neighbour to consider discarding | ||
502 | */ | ||
503 | static void | ||
504 | consider_free_neighbour (struct Neighbour *n) | ||
505 | { | ||
506 | struct GNUNET_TIME_Relative left; | ||
507 | |||
508 | if ((n->th != NULL) || (n->pitr != NULL) || (GNUNET_YES == n->is_connected)) | ||
509 | return; /* no chance */ | ||
510 | |||
511 | left = GNUNET_TIME_absolute_get_remaining (get_neighbour_timeout (n)); | ||
512 | if (left.rel_value > 0) | ||
513 | { | ||
514 | if (n->dead_clean_task != GNUNET_SCHEDULER_NO_TASK) | ||
515 | GNUNET_SCHEDULER_cancel (n->dead_clean_task); | ||
516 | n->dead_clean_task = | ||
517 | GNUNET_SCHEDULER_add_delayed (left, &consider_free_task, n); | ||
518 | return; | ||
519 | } | ||
520 | /* actually free the neighbour... */ | ||
521 | GNUNET_assert (GNUNET_YES == | ||
522 | GNUNET_CONTAINER_multihashmap_remove (neighbours, | ||
523 | &n->peer.hashPubKey, n)); | ||
524 | GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), | ||
525 | GNUNET_CONTAINER_multihashmap_size (neighbours), | ||
526 | GNUNET_NO); | ||
527 | free_neighbour (n); | ||
528 | } | ||
529 | |||
530 | |||
531 | /** | ||
532 | * Function called when the transport service is ready to | ||
533 | * receive an encrypted message for the respective peer | ||
534 | * | ||
535 | * @param cls neighbour to use message from | ||
536 | * @param size number of bytes we can transmit | ||
537 | * @param buf where to copy the message | ||
538 | * @return number of bytes transmitted | ||
539 | */ | ||
540 | static size_t | ||
541 | notify_encrypted_transmit_ready (void *cls, size_t size, void *buf) | ||
542 | { | ||
543 | struct Neighbour *n = cls; | ||
544 | struct MessageEntry *m; | ||
545 | size_t ret; | ||
546 | char *cbuf; | ||
547 | |||
548 | n->th = NULL; | ||
549 | m = n->encrypted_head; | ||
550 | if (m == NULL) | ||
551 | { | ||
552 | #if DEBUG_CORE | ||
553 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
554 | "Encrypted message queue empty, no messages added to buffer for `%4s'\n", | ||
555 | GNUNET_i2s (&n->peer)); | ||
556 | #endif | ||
557 | return 0; | ||
558 | } | ||
559 | GNUNET_CONTAINER_DLL_remove (n->encrypted_head, n->encrypted_tail, m); | ||
560 | ret = 0; | ||
561 | cbuf = buf; | ||
562 | if (buf != NULL) | ||
563 | { | ||
564 | GNUNET_assert (size >= m->size); | ||
565 | memcpy (cbuf, &m[1], m->size); | ||
566 | ret = m->size; | ||
567 | GNUNET_BANDWIDTH_tracker_consume (&n->available_send_window, m->size); | ||
568 | #if DEBUG_CORE | ||
569 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
570 | "Copied message of type %u and size %u into transport buffer for `%4s'\n", | ||
571 | (unsigned int) | ||
572 | ntohs (((struct GNUNET_MessageHeader *) &m[1])->type), | ||
573 | (unsigned int) ret, GNUNET_i2s (&n->peer)); | ||
574 | #endif | ||
575 | process_encrypted_neighbour_queue (n); | ||
576 | } | ||
577 | else | ||
578 | { | ||
579 | #if DEBUG_CORE | ||
580 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
581 | "Transmission of message of type %u and size %u failed\n", | ||
582 | (unsigned int) | ||
583 | ntohs (((struct GNUNET_MessageHeader *) &m[1])->type), | ||
584 | (unsigned int) m->size); | ||
585 | #endif | ||
586 | } | ||
587 | GNUNET_free (m); | ||
588 | consider_free_neighbour (n); | ||
589 | GNUNET_STATISTICS_update (stats, | ||
590 | gettext_noop | ||
591 | ("# encrypted bytes given to transport"), ret, | ||
592 | GNUNET_NO); | ||
593 | return ret; | ||
594 | } | ||
595 | |||
596 | |||
597 | /** | ||
598 | * Check if we have encrypted messages for the specified neighbour | ||
599 | * pending, and if so, check with the transport about sending them | ||
600 | * out. | ||
601 | * | ||
602 | * @param n neighbour to check. | ||
603 | */ | ||
604 | static void | ||
605 | process_encrypted_neighbour_queue (struct Neighbour *n) | ||
606 | { | ||
607 | struct MessageEntry *m; | ||
608 | |||
609 | if (n->th != NULL) | ||
610 | return; /* request already pending */ | ||
611 | if (GNUNET_YES != n->is_connected) | ||
612 | { | ||
613 | GNUNET_break (0); | ||
614 | return; | ||
615 | } | ||
616 | m = n->encrypted_head; | ||
617 | if (m == NULL) | ||
618 | { | ||
619 | /* encrypted queue empty, try plaintext instead */ | ||
620 | process_plaintext_neighbour_queue (n); | ||
621 | return; | ||
622 | } | ||
623 | #if DEBUG_CORE > 1 | ||
624 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
625 | "Asking transport for transmission of %u bytes to `%4s' in next %llu ms\n", | ||
626 | (unsigned int) m->size, GNUNET_i2s (&n->peer), | ||
627 | (unsigned long long) | ||
628 | GNUNET_TIME_absolute_get_remaining (m->deadline).rel_value); | ||
629 | #endif | ||
630 | n->th = | ||
631 | GNUNET_TRANSPORT_notify_transmit_ready (transport, &n->peer, m->size, | ||
632 | m->priority, | ||
633 | GNUNET_TIME_absolute_get_remaining | ||
634 | (m->deadline), | ||
635 | ¬ify_encrypted_transmit_ready, | ||
636 | n); | ||
637 | if (n->th == NULL) | ||
638 | { | ||
639 | /* message request too large or duplicate request */ | ||
640 | GNUNET_break (0); | ||
641 | /* discard encrypted message */ | ||
642 | GNUNET_CONTAINER_DLL_remove (n->encrypted_head, n->encrypted_tail, m); | ||
643 | GNUNET_free (m); | ||
644 | process_encrypted_neighbour_queue (n); | ||
645 | } | ||
646 | } | ||
647 | |||
648 | |||
649 | /** | ||
650 | * Initialize a new 'struct Neighbour'. | ||
651 | * | ||
652 | * @param pid ID of the new neighbour | ||
653 | * @return handle for the new neighbour | ||
654 | */ | ||
655 | static struct Neighbour * | ||
656 | create_neighbour (const struct GNUNET_PeerIdentity *pid) | ||
657 | { | ||
658 | struct Neighbour *n; | ||
659 | struct GNUNET_TIME_Absolute now; | ||
660 | |||
661 | #if DEBUG_CORE | ||
662 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
663 | "Creating neighbour entry for peer `%4s'\n", GNUNET_i2s (pid)); | ||
664 | #endif | ||
665 | n = GNUNET_malloc (sizeof (struct Neighbour)); | ||
666 | n->peer = *pid; | ||
667 | GNUNET_CRYPTO_aes_create_session_key (&n->encrypt_key); | ||
668 | now = GNUNET_TIME_absolute_get (); | ||
669 | n->encrypt_key_created = now; | ||
670 | n->last_activity = now; | ||
671 | n->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY; | ||
672 | n->bw_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT; | ||
673 | n->bw_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT; | ||
674 | n->bw_out_internal_limit = GNUNET_BANDWIDTH_value_init (UINT32_MAX); | ||
675 | n->bw_out_external_limit = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT; | ||
676 | n->ping_challenge = | ||
677 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); | ||
678 | GNUNET_assert (GNUNET_OK == | ||
679 | GNUNET_CONTAINER_multihashmap_put (neighbours, | ||
680 | &n->peer.hashPubKey, n, | ||
681 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
682 | GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), | ||
683 | GNUNET_CONTAINER_multihashmap_size (neighbours), | ||
684 | GNUNET_NO); | ||
685 | neighbour_quota_update (n, NULL); | ||
686 | consider_free_neighbour (n); | ||
687 | return n; | ||
688 | } | ||
689 | |||
690 | |||
691 | int | ||
692 | GSC_NEIGHBOURS_init () | ||
693 | { | ||
694 | neighbours = GNUNET_CONTAINER_multihashmap_create (128); | ||
695 | self.public_key = &my_public_key; | ||
696 | self.peer = my_identity; | ||
697 | self.last_activity = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
698 | self.status = PEER_STATE_KEY_CONFIRMED; | ||
699 | self.is_connected = GNUNET_YES; | ||
700 | return GNUNET_OK; | ||
701 | } | ||
702 | |||
703 | |||
704 | void | ||
705 | GSC_NEIGHBOURS_done () | ||
706 | { | ||
707 | GNUNET_CONTAINER_multihashmap_iterate (neighbours, &free_neighbour_helper, | ||
708 | NULL); | ||
709 | GNUNET_CONTAINER_multihashmap_destroy (neighbours); | ||
710 | neighbours = NULL; | ||
711 | GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), | ||
712 | 0, GNUNET_NO); | ||
713 | } | ||
diff --git a/src/core/gnunet-service-core_typemap.c b/src/core/gnunet-service-core_typemap.c new file mode 100644 index 000000000..3aa652999 --- /dev/null +++ b/src/core/gnunet-service-core_typemap.c | |||
@@ -0,0 +1,96 @@ | |||
1 | |||
2 | /** | ||
3 | * Bitmap of message types this peer is able to handle. | ||
4 | */ | ||
5 | static uint32_t my_type_map[(UINT16_MAX + 1) / 32]; | ||
6 | |||
7 | |||
8 | /** | ||
9 | * Compute a type map message for this peer. | ||
10 | * | ||
11 | * @return this peers current type map message. | ||
12 | */ | ||
13 | static struct GNUNET_MessageHeader * | ||
14 | compute_type_map_message () | ||
15 | { | ||
16 | char *tmp; | ||
17 | uLongf dlen; | ||
18 | struct GNUNET_MessageHeader *hdr; | ||
19 | |||
20 | #ifdef compressBound | ||
21 | dlen = compressBound (sizeof (my_type_map)); | ||
22 | #else | ||
23 | dlen = sizeof (my_type_map) + (sizeof (my_type_map) / 100) + 20; | ||
24 | /* documentation says 100.1% oldSize + 12 bytes, but we | ||
25 | * should be able to overshoot by more to be safe */ | ||
26 | #endif | ||
27 | hdr = GNUNET_malloc (dlen + sizeof (struct GNUNET_MessageHeader)); | ||
28 | hdr->size = htons ((uint16_t) dlen + sizeof (struct GNUNET_MessageHeader)); | ||
29 | tmp = (char *) &hdr[1]; | ||
30 | if ((Z_OK != | ||
31 | compress2 ((Bytef *) tmp, &dlen, (const Bytef *) my_type_map, | ||
32 | sizeof (my_type_map), 9)) || (dlen >= sizeof (my_type_map))) | ||
33 | { | ||
34 | dlen = sizeof (my_type_map); | ||
35 | memcpy (tmp, my_type_map, sizeof (my_type_map)); | ||
36 | hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP); | ||
37 | } | ||
38 | else | ||
39 | { | ||
40 | hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP); | ||
41 | } | ||
42 | return hdr; | ||
43 | } | ||
44 | |||
45 | |||
46 | /** | ||
47 | * Send a type map message to the neighbour. | ||
48 | * | ||
49 | * @param cls the type map message | ||
50 | * @param key neighbour's identity | ||
51 | * @param value 'struct Neighbour' of the target | ||
52 | * @return always GNUNET_OK | ||
53 | */ | ||
54 | static int | ||
55 | send_type_map_to_neighbour (void *cls, const GNUNET_HashCode * key, void *value) | ||
56 | { | ||
57 | struct GNUNET_MessageHeader *hdr = cls; | ||
58 | struct Neighbour *n = value; | ||
59 | struct MessageEntry *m; | ||
60 | uint16_t size; | ||
61 | |||
62 | if (n == &self) | ||
63 | return GNUNET_OK; | ||
64 | size = ntohs (hdr->size); | ||
65 | m = GNUNET_malloc (sizeof (struct MessageEntry) + size); | ||
66 | memcpy (&m[1], hdr, size); | ||
67 | m->deadline = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
68 | m->slack_deadline = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
69 | m->priority = UINT_MAX; | ||
70 | m->sender_status = n->status; | ||
71 | m->size = size; | ||
72 | m->next = n->messages; | ||
73 | n->messages = m; | ||
74 | return GNUNET_OK; | ||
75 | } | ||
76 | |||
77 | |||
78 | |||
79 | /** | ||
80 | * Send my type map to all connected peers (it got changed). | ||
81 | */ | ||
82 | static void | ||
83 | broadcast_my_type_map () | ||
84 | { | ||
85 | struct GNUNET_MessageHeader *hdr; | ||
86 | |||
87 | if (NULL == neighbours) | ||
88 | return; | ||
89 | hdr = compute_type_map_message (); | ||
90 | GNUNET_CONTAINER_multihashmap_iterate (neighbours, | ||
91 | &send_type_map_to_neighbour, hdr); | ||
92 | GNUNET_free (hdr); | ||
93 | } | ||
94 | |||
95 | |||
96 | |||