diff options
Diffstat (limited to 'src/service/core')
36 files changed, 9925 insertions, 0 deletions
diff --git a/src/service/core/.gitignore b/src/service/core/.gitignore new file mode 100644 index 000000000..e985f5ff4 --- /dev/null +++ b/src/service/core/.gitignore | |||
@@ -0,0 +1,9 @@ | |||
1 | gnunet-service-core | ||
2 | core.conf | ||
3 | test_core_api | ||
4 | test_core_api_reliability | ||
5 | test_core_api_send_to_self | ||
6 | test_core_api_start_only | ||
7 | test_core_quota_compliance_asymmetric_recv_limited | ||
8 | test_core_quota_compliance_asymmetric_send_limited | ||
9 | test_core_quota_compliance_symmetric | ||
diff --git a/src/service/core/Makefile.am b/src/service/core/Makefile.am new file mode 100644 index 000000000..95f22c82c --- /dev/null +++ b/src/service/core/Makefile.am | |||
@@ -0,0 +1,131 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
5 | |||
6 | plugindir = $(libdir)/gnunet | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | pkgcfg_DATA = \ | ||
11 | core.conf | ||
12 | |||
13 | if USE_COVERAGE | ||
14 | AM_CFLAGS = --coverage -O0 | ||
15 | XLIB = -lgcov | ||
16 | endif | ||
17 | |||
18 | plugin_LTLIBRARIES = \ | ||
19 | libgnunet_test_core_plugin_cmd_just_run.la | ||
20 | |||
21 | TESTING_LIBS = \ | ||
22 | libgnunetcoretesting.la | ||
23 | |||
24 | lib_LTLIBRARIES = \ | ||
25 | libgnunetcore.la \ | ||
26 | $(TESTING_LIBS) | ||
27 | |||
28 | libgnunetcore_la_SOURCES = \ | ||
29 | core_api.c core.h \ | ||
30 | core_api_monitor_peers.c | ||
31 | libgnunetcore_la_LIBADD = \ | ||
32 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
33 | $(GN_LIBINTL) $(XLIB) | ||
34 | libgnunetcore_la_LDFLAGS = \ | ||
35 | $(GN_LIB_LDFLAGS) \ | ||
36 | -version-info 0:1:0 | ||
37 | |||
38 | libgnunet_test_core_plugin_cmd_just_run_la_SOURCES = \ | ||
39 | test_core_plugin_cmd_just_run.c | ||
40 | libgnunet_test_core_plugin_cmd_just_run_la_LIBADD = \ | ||
41 | libgnunetcoretesting.la \ | ||
42 | $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ | ||
43 | $(top_builddir)/src/service/transport/libgnunettransportcore.la \ | ||
44 | $(top_builddir)/src/lib/hello/libgnunethello.la \ | ||
45 | $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ | ||
46 | $(top_builddir)/src/service/transport/libgnunettransporttesting2.la \ | ||
47 | $(top_builddir)/src/lib/testing/libgnunettesting.la \ | ||
48 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
49 | $(top_builddir)/src/service/arm/libgnunetarm.la \ | ||
50 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
51 | $(LTLIBINTL) | ||
52 | libgnunet_test_core_plugin_cmd_just_run_la_LDFLAGS = \ | ||
53 | $(GN_PLUGIN_LDFLAGS) | ||
54 | |||
55 | libgnunetcoretesting_la_SOURCES = \ | ||
56 | core_api_cmd_connecting_peers.c | ||
57 | libgnunetcoretesting_la_LIBADD = \ | ||
58 | $(top_builddir)/src/lib/testing/libgnunettesting.la \ | ||
59 | $(top_builddir)/src/service/arm/libgnunetarm.la \ | ||
60 | $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ | ||
61 | $(top_builddir)/src/service/transport/libgnunettransporttesting2.la \ | ||
62 | $(top_builddir)/src/lib/hello/libgnunethello.la \ | ||
63 | $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ | ||
64 | $(top_builddir)/src/service/transport/libgnunettransportcore.la \ | ||
65 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
66 | libgnunetcoretesting_la_LDFLAGS = \ | ||
67 | $(GN_LIBINTL) \ | ||
68 | $(GN_LIB_LDFLAGS) \ | ||
69 | -version-info 0:0:0 | ||
70 | |||
71 | |||
72 | libexec_PROGRAMS = \ | ||
73 | gnunet-service-core | ||
74 | |||
75 | gnunet_service_core_SOURCES = \ | ||
76 | gnunet-service-core.c gnunet-service-core.h \ | ||
77 | gnunet-service-core_kx.c gnunet-service-core_kx.h \ | ||
78 | gnunet-service-core_sessions.c gnunet-service-core_sessions.h \ | ||
79 | gnunet-service-core_typemap.c gnunet-service-core_typemap.h | ||
80 | gnunet_service_core_LDADD = \ | ||
81 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
82 | $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ | ||
83 | $(top_builddir)/src/service/transport/libgnunettransportcore.la \ | ||
84 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
85 | $(GN_LIBINTL) $(Z_LIBS) | ||
86 | |||
87 | |||
88 | TESTING_TESTS = \ | ||
89 | test_core_api_send_to_self | ||
90 | |||
91 | check_PROGRAMS = \ | ||
92 | test_core_api_start_only \ | ||
93 | $(TESTING_TESTS) | ||
94 | |||
95 | # Only test TNG if we run experimental | ||
96 | #check_SCRIPTS= \ | ||
97 | # test_core_start_testcase.sh | ||
98 | |||
99 | if ENABLE_TEST_RUN | ||
100 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
101 | TESTS = $(check_PROGRAMS) \ | ||
102 | $(check_SCRIPTS) | ||
103 | endif | ||
104 | |||
105 | test_core_api_send_to_self_SOURCES = \ | ||
106 | test_core_api_send_to_self.c | ||
107 | test_core_api_send_to_self_LDADD = \ | ||
108 | libgnunetcore.la \ | ||
109 | $(top_builddir)/src/lib/testing/libgnunettesting.la \ | ||
110 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
111 | |||
112 | test_core_api_start_only_SOURCES = \ | ||
113 | test_core_api_start_only.c | ||
114 | test_core_api_start_only_LDADD = \ | ||
115 | $(top_builddir)/src/lib/testing/libgnunettesting.la \ | ||
116 | libgnunetcore.la \ | ||
117 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
118 | |||
119 | EXTRA_DIST = \ | ||
120 | test_core_start_testcase.sh \ | ||
121 | test_core_defaults.conf \ | ||
122 | test_core_api_data.conf \ | ||
123 | test_core_api_peer1.conf \ | ||
124 | test_core_api_peer2.conf \ | ||
125 | test_core_api_send_to_self.conf \ | ||
126 | test_core_quota_asymmetric_recv_limited_peer1.conf \ | ||
127 | test_core_quota_asymmetric_recv_limited_peer2.conf \ | ||
128 | test_core_quota_asymmetric_send_limit_peer1.conf \ | ||
129 | test_core_quota_asymmetric_send_limit_peer2.conf \ | ||
130 | test_core_quota_peer1.conf \ | ||
131 | test_core_quota_peer2.conf | ||
diff --git a/src/service/core/core.conf.in b/src/service/core/core.conf.in new file mode 100644 index 000000000..00faf6079 --- /dev/null +++ b/src/service/core/core.conf.in | |||
@@ -0,0 +1,23 @@ | |||
1 | [core] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | @JAVAPORT@PORT = 2092 | ||
4 | HOSTNAME = localhost | ||
5 | BINARY = gnunet-service-core | ||
6 | ACCEPT_FROM = 127.0.0.1; | ||
7 | ACCEPT_FROM6 = ::1; | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-core.sock | ||
9 | UNIX_MATCH_UID = NO | ||
10 | UNIX_MATCH_GID = YES | ||
11 | # DISABLE_SOCKET_FORWARDING = NO | ||
12 | # USERNAME = | ||
13 | # MAXBUF = | ||
14 | # TIMEOUT = | ||
15 | # DISABLEV6 = | ||
16 | # BINDTO = | ||
17 | # REJECT_FROM = | ||
18 | # REJECT_FROM6 = | ||
19 | # PREFIX = | ||
20 | |||
21 | # Note: this MUST be set to YES in production, only set to NO for testing | ||
22 | # for performance (testbed/cluster-scale use!). | ||
23 | USE_EPHEMERAL_KEYS = YES | ||
diff --git a/src/service/core/core.h b/src/service/core/core.h new file mode 100644 index 000000000..d4596f038 --- /dev/null +++ b/src/service/core/core.h | |||
@@ -0,0 +1,326 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009-2014 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/core.h | ||
23 | * @brief common internal definitions for core service | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef CORE_H | ||
27 | #define CORE_H | ||
28 | |||
29 | #include "gnunet_util_lib.h" | ||
30 | #include "gnunet_time_lib.h" | ||
31 | |||
32 | /** | ||
33 | * General core debugging. | ||
34 | */ | ||
35 | #define DEBUG_CORE GNUNET_EXTRA_LOGGING | ||
36 | |||
37 | /** | ||
38 | * Definition of bits in the InitMessage's options field that specify | ||
39 | * which events this client cares about. Note that inbound messages | ||
40 | * for handlers that were specifically registered are always | ||
41 | * transmitted to the client. | ||
42 | */ | ||
43 | #define GNUNET_CORE_OPTION_NOTHING 0 | ||
44 | |||
45 | /** | ||
46 | * Client cares about connectivity changes. | ||
47 | */ | ||
48 | #define GNUNET_CORE_OPTION_SEND_STATUS_CHANGE 4 | ||
49 | |||
50 | /** | ||
51 | * Client wants all inbound messages in full. | ||
52 | */ | ||
53 | #define GNUNET_CORE_OPTION_SEND_FULL_INBOUND 8 | ||
54 | |||
55 | /** | ||
56 | * Client just wants the 4-byte message headers of | ||
57 | * all inbound messages. | ||
58 | */ | ||
59 | #define GNUNET_CORE_OPTION_SEND_HDR_INBOUND 16 | ||
60 | |||
61 | /** | ||
62 | * Client wants all outbound messages in full. | ||
63 | */ | ||
64 | #define GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND 32 | ||
65 | |||
66 | /** | ||
67 | * Client just wants the 4-byte message headers of | ||
68 | * all outbound messages. | ||
69 | */ | ||
70 | #define GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND 64 | ||
71 | |||
72 | |||
73 | GNUNET_NETWORK_STRUCT_BEGIN | ||
74 | |||
75 | /** | ||
76 | * Message transmitted core clients to gnunet-service-core | ||
77 | * to start the interaction. This header is followed by | ||
78 | * uint16_t type values specifying which messages this | ||
79 | * client is interested in. | ||
80 | */ | ||
81 | struct InitMessage | ||
82 | { | ||
83 | /** | ||
84 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_INIT. | ||
85 | */ | ||
86 | struct GNUNET_MessageHeader header; | ||
87 | |||
88 | /** | ||
89 | * Options, see GNUNET_CORE_OPTION_ values. | ||
90 | */ | ||
91 | uint32_t options GNUNET_PACKED; | ||
92 | }; | ||
93 | |||
94 | |||
95 | /** | ||
96 | * Message transmitted by the gnunet-service-core process | ||
97 | * to its clients in response to an INIT message. | ||
98 | */ | ||
99 | struct InitReplyMessage | ||
100 | { | ||
101 | /** | ||
102 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY | ||
103 | */ | ||
104 | struct GNUNET_MessageHeader header; | ||
105 | |||
106 | /** | ||
107 | * Always zero. | ||
108 | */ | ||
109 | uint32_t reserved GNUNET_PACKED; | ||
110 | |||
111 | /** | ||
112 | * Public key of the local peer. | ||
113 | */ | ||
114 | struct GNUNET_PeerIdentity my_identity; | ||
115 | }; | ||
116 | |||
117 | |||
118 | /** | ||
119 | * Message sent by the service to clients to notify them | ||
120 | * about a peer connecting. | ||
121 | */ | ||
122 | struct ConnectNotifyMessage | ||
123 | { | ||
124 | /** | ||
125 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT | ||
126 | */ | ||
127 | struct GNUNET_MessageHeader header; | ||
128 | |||
129 | /** | ||
130 | * Always zero. | ||
131 | */ | ||
132 | uint32_t reserved GNUNET_PACKED; | ||
133 | |||
134 | /** | ||
135 | * Identity of the connecting peer. | ||
136 | */ | ||
137 | struct GNUNET_PeerIdentity peer; | ||
138 | }; | ||
139 | |||
140 | |||
141 | /** | ||
142 | * Message sent by the service to clients to notify them | ||
143 | * about a peer disconnecting. | ||
144 | */ | ||
145 | struct DisconnectNotifyMessage | ||
146 | { | ||
147 | /** | ||
148 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT. | ||
149 | */ | ||
150 | struct GNUNET_MessageHeader header; | ||
151 | |||
152 | /** | ||
153 | * Always zero. | ||
154 | */ | ||
155 | uint32_t reserved GNUNET_PACKED; | ||
156 | |||
157 | /** | ||
158 | * Identity of the connecting peer. | ||
159 | */ | ||
160 | struct GNUNET_PeerIdentity peer; | ||
161 | }; | ||
162 | |||
163 | |||
164 | /** | ||
165 | * Message sent by the service to clients to notify them about | ||
166 | * messages being received or transmitted. This overall message is | ||
167 | * followed by the real message, or just the header of the real | ||
168 | * message (depending on the client's preferences). The receiver can | ||
169 | * tell if it got the full message or only a partial message by | ||
170 | * looking at the size field in the header of NotifyTrafficMessage and | ||
171 | * checking it with the size field in the message that follows. | ||
172 | */ | ||
173 | struct NotifyTrafficMessage | ||
174 | { | ||
175 | /** | ||
176 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND | ||
177 | * or #GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND. | ||
178 | */ | ||
179 | struct GNUNET_MessageHeader header; | ||
180 | |||
181 | /** | ||
182 | * Identity of the receiver or sender. | ||
183 | */ | ||
184 | struct GNUNET_PeerIdentity peer; | ||
185 | |||
186 | /* Followed by payload (message or just header), variable size */ | ||
187 | }; | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Client notifying core about the maximum-priority | ||
192 | * message it has in the queue for a particular target. | ||
193 | */ | ||
194 | struct SendMessageRequest | ||
195 | { | ||
196 | /** | ||
197 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST | ||
198 | */ | ||
199 | struct GNUNET_MessageHeader header; | ||
200 | |||
201 | /** | ||
202 | * How important is this message? | ||
203 | */ | ||
204 | uint32_t priority GNUNET_PACKED; | ||
205 | |||
206 | /** | ||
207 | * By what time would the sender really like to see this | ||
208 | * message transmitted? | ||
209 | */ | ||
210 | struct GNUNET_TIME_AbsoluteNBO deadline; | ||
211 | |||
212 | /** | ||
213 | * Identity of the intended target. | ||
214 | */ | ||
215 | struct GNUNET_PeerIdentity peer; | ||
216 | |||
217 | /** | ||
218 | * Always zero. | ||
219 | */ | ||
220 | uint32_t reserved GNUNET_PACKED; | ||
221 | |||
222 | /** | ||
223 | * How large is the message? | ||
224 | */ | ||
225 | uint16_t size GNUNET_PACKED; | ||
226 | |||
227 | /** | ||
228 | * Counter for this peer to match SMRs to replies. | ||
229 | */ | ||
230 | uint16_t smr_id GNUNET_PACKED; | ||
231 | }; | ||
232 | |||
233 | |||
234 | /** | ||
235 | * Core notifying client that it is allowed to now | ||
236 | * transmit a message to the given target | ||
237 | * (response to #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST). | ||
238 | */ | ||
239 | struct SendMessageReady | ||
240 | { | ||
241 | /** | ||
242 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_SEND_READY | ||
243 | */ | ||
244 | struct GNUNET_MessageHeader header; | ||
245 | |||
246 | /** | ||
247 | * How many bytes are allowed for transmission? | ||
248 | * Guaranteed to be at least as big as the requested size, | ||
249 | * or ZERO if the request is rejected (will timeout, | ||
250 | * peer disconnected, queue full, etc.). | ||
251 | */ | ||
252 | uint16_t size GNUNET_PACKED; | ||
253 | |||
254 | /** | ||
255 | * smr_id from the request. | ||
256 | */ | ||
257 | uint16_t smr_id GNUNET_PACKED; | ||
258 | |||
259 | /** | ||
260 | * Identity of the intended target. | ||
261 | */ | ||
262 | struct GNUNET_PeerIdentity peer; | ||
263 | }; | ||
264 | |||
265 | |||
266 | /** | ||
267 | * Client asking core to transmit a particular message to a particular | ||
268 | * target (response to #GNUNET_MESSAGE_TYPE_CORE_SEND_READY). | ||
269 | */ | ||
270 | struct SendMessage | ||
271 | { | ||
272 | /** | ||
273 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_SEND | ||
274 | */ | ||
275 | struct GNUNET_MessageHeader header; | ||
276 | |||
277 | /** | ||
278 | * How important is this message? Contains a | ||
279 | * `enum GNUNET_MQ_PriorityPreferences` in NBO. | ||
280 | */ | ||
281 | uint32_t priority GNUNET_PACKED; | ||
282 | |||
283 | /** | ||
284 | * By what time would the sender really like to see this | ||
285 | * message transmitted? | ||
286 | */ | ||
287 | struct GNUNET_TIME_AbsoluteNBO deadline; | ||
288 | |||
289 | /** | ||
290 | * Identity of the intended receiver. | ||
291 | */ | ||
292 | struct GNUNET_PeerIdentity peer; | ||
293 | }; | ||
294 | |||
295 | |||
296 | /** | ||
297 | * Message sent by the service to monitor clients to notify them | ||
298 | * about a peer changing status. | ||
299 | */ | ||
300 | struct MonitorNotifyMessage | ||
301 | { | ||
302 | /** | ||
303 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY | ||
304 | */ | ||
305 | struct GNUNET_MessageHeader header; | ||
306 | |||
307 | /** | ||
308 | * New peer state, an `enum GNUNET_CORE_KxState` in NBO. | ||
309 | */ | ||
310 | uint32_t state GNUNET_PACKED; | ||
311 | |||
312 | /** | ||
313 | * Identity of the peer. | ||
314 | */ | ||
315 | struct GNUNET_PeerIdentity peer; | ||
316 | |||
317 | /** | ||
318 | * How long will we stay in this state (if nothing else happens)? | ||
319 | */ | ||
320 | struct GNUNET_TIME_AbsoluteNBO timeout; | ||
321 | }; | ||
322 | |||
323 | |||
324 | GNUNET_NETWORK_STRUCT_END | ||
325 | #endif | ||
326 | /* end of core.h */ | ||
diff --git a/src/service/core/core_api.c b/src/service/core/core_api.c new file mode 100644 index 000000000..2e0bb1785 --- /dev/null +++ b/src/service/core/core_api.c | |||
@@ -0,0 +1,772 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009-2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file core/core_api.c | ||
22 | * @brief core service; this is the main API for encrypted P2P | ||
23 | * communications | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_constants.h" | ||
29 | #include "gnunet_core_service.h" | ||
30 | #include "core.h" | ||
31 | |||
32 | #define LOG(kind, ...) GNUNET_log_from (kind, "core-api", __VA_ARGS__) | ||
33 | |||
34 | |||
35 | /** | ||
36 | * Information we track for each peer. | ||
37 | */ | ||
38 | struct PeerRecord | ||
39 | { | ||
40 | /** | ||
41 | * Corresponding CORE handle. | ||
42 | */ | ||
43 | struct GNUNET_CORE_Handle *h; | ||
44 | |||
45 | /** | ||
46 | * Message queue for the peer. | ||
47 | */ | ||
48 | struct GNUNET_MQ_Handle *mq; | ||
49 | |||
50 | /** | ||
51 | * Message we are currently trying to pass to the CORE service | ||
52 | * for this peer (from @e mq). | ||
53 | */ | ||
54 | struct GNUNET_MQ_Envelope *env; | ||
55 | |||
56 | /** | ||
57 | * Value the client returned when we connected, used | ||
58 | * as the closure in various places. | ||
59 | */ | ||
60 | void *client_cls; | ||
61 | |||
62 | /** | ||
63 | * Peer the record is about. | ||
64 | */ | ||
65 | struct GNUNET_PeerIdentity peer; | ||
66 | |||
67 | /** | ||
68 | * SendMessageRequest ID generator for this peer. | ||
69 | */ | ||
70 | uint16_t smr_id_gen; | ||
71 | }; | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Context for the core service connection. | ||
76 | */ | ||
77 | struct GNUNET_CORE_Handle | ||
78 | { | ||
79 | /** | ||
80 | * Configuration we're using. | ||
81 | */ | ||
82 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
83 | |||
84 | /** | ||
85 | * Closure for the various callbacks. | ||
86 | */ | ||
87 | void *cls; | ||
88 | |||
89 | /** | ||
90 | * Function to call once we've handshaked with the core service. | ||
91 | */ | ||
92 | GNUNET_CORE_StartupCallback init; | ||
93 | |||
94 | /** | ||
95 | * Function to call whenever we're notified about a peer connecting. | ||
96 | */ | ||
97 | GNUNET_CORE_ConnectEventHandler connects; | ||
98 | |||
99 | /** | ||
100 | * Function to call whenever we're notified about a peer disconnecting. | ||
101 | */ | ||
102 | GNUNET_CORE_DisconnectEventHandler disconnects; | ||
103 | |||
104 | /** | ||
105 | * Function handlers for messages of particular type. | ||
106 | */ | ||
107 | struct GNUNET_MQ_MessageHandler *handlers; | ||
108 | |||
109 | /** | ||
110 | * Our message queue for transmissions to the service. | ||
111 | */ | ||
112 | struct GNUNET_MQ_Handle *mq; | ||
113 | |||
114 | /** | ||
115 | * Hash map listing all of the peers that we are currently | ||
116 | * connected to. | ||
117 | */ | ||
118 | struct GNUNET_CONTAINER_MultiPeerMap *peers; | ||
119 | |||
120 | /** | ||
121 | * Identity of this peer. | ||
122 | */ | ||
123 | struct GNUNET_PeerIdentity me; | ||
124 | |||
125 | /** | ||
126 | * ID of reconnect task (if any). | ||
127 | */ | ||
128 | struct GNUNET_SCHEDULER_Task *reconnect_task; | ||
129 | |||
130 | /** | ||
131 | * Current delay we use for re-trying to connect to core. | ||
132 | */ | ||
133 | struct GNUNET_TIME_Relative retry_backoff; | ||
134 | |||
135 | /** | ||
136 | * Number of entries in the handlers array. | ||
137 | */ | ||
138 | unsigned int hcnt; | ||
139 | |||
140 | /** | ||
141 | * Did we ever get INIT? | ||
142 | */ | ||
143 | int have_init; | ||
144 | }; | ||
145 | |||
146 | |||
147 | /** | ||
148 | * Our current client connection went down. Clean it up | ||
149 | * and try to reconnect! | ||
150 | * | ||
151 | * @param h our handle to the core service | ||
152 | */ | ||
153 | static void | ||
154 | reconnect (struct GNUNET_CORE_Handle *h); | ||
155 | |||
156 | |||
157 | /** | ||
158 | * Task schedule to try to re-connect to core. | ||
159 | * | ||
160 | * @param cls the `struct GNUNET_CORE_Handle` | ||
161 | */ | ||
162 | static void | ||
163 | reconnect_task (void *cls) | ||
164 | { | ||
165 | struct GNUNET_CORE_Handle *h = cls; | ||
166 | |||
167 | h->reconnect_task = NULL; | ||
168 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service after delay\n"); | ||
169 | reconnect (h); | ||
170 | } | ||
171 | |||
172 | |||
173 | /** | ||
174 | * Notify clients about disconnect and free the entry for connected | ||
175 | * peer. | ||
176 | * | ||
177 | * @param cls the `struct GNUNET_CORE_Handle *` | ||
178 | * @param key the peer identity (not used) | ||
179 | * @param value the `struct PeerRecord` to free. | ||
180 | * @return #GNUNET_YES (continue) | ||
181 | */ | ||
182 | static int | ||
183 | disconnect_and_free_peer_entry (void *cls, | ||
184 | const struct GNUNET_PeerIdentity *key, | ||
185 | void *value) | ||
186 | { | ||
187 | struct GNUNET_CORE_Handle *h = cls; | ||
188 | struct PeerRecord *pr = value; | ||
189 | |||
190 | GNUNET_assert (pr->h == h); | ||
191 | if (NULL != h->disconnects) | ||
192 | h->disconnects (h->cls, &pr->peer, pr->client_cls); | ||
193 | GNUNET_assert (GNUNET_YES == | ||
194 | GNUNET_CONTAINER_multipeermap_remove (h->peers, key, pr)); | ||
195 | GNUNET_MQ_destroy (pr->mq); | ||
196 | GNUNET_assert (NULL == pr->mq); | ||
197 | if (NULL != pr->env) | ||
198 | { | ||
199 | GNUNET_MQ_discard (pr->env); | ||
200 | pr->env = NULL; | ||
201 | } | ||
202 | GNUNET_free (pr); | ||
203 | return GNUNET_YES; | ||
204 | } | ||
205 | |||
206 | |||
207 | /** | ||
208 | * Close down any existing connection to the CORE service and | ||
209 | * try re-establishing it later. | ||
210 | * | ||
211 | * @param h our handle | ||
212 | */ | ||
213 | static void | ||
214 | reconnect_later (struct GNUNET_CORE_Handle *h) | ||
215 | { | ||
216 | GNUNET_assert (NULL == h->reconnect_task); | ||
217 | if (NULL != h->mq) | ||
218 | { | ||
219 | GNUNET_MQ_destroy (h->mq); | ||
220 | h->mq = NULL; | ||
221 | } | ||
222 | GNUNET_assert (NULL == h->reconnect_task); | ||
223 | h->reconnect_task = | ||
224 | GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_task, h); | ||
225 | GNUNET_CONTAINER_multipeermap_iterate (h->peers, | ||
226 | &disconnect_and_free_peer_entry, | ||
227 | h); | ||
228 | h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff); | ||
229 | } | ||
230 | |||
231 | |||
232 | /** | ||
233 | * Error handler for the message queue to the CORE service. | ||
234 | * On errors, we reconnect. | ||
235 | * | ||
236 | * @param cls closure, a `struct GNUNET_CORE_Handle *` | ||
237 | * @param error error code | ||
238 | */ | ||
239 | static void | ||
240 | handle_mq_error (void *cls, enum GNUNET_MQ_Error error) | ||
241 | { | ||
242 | struct GNUNET_CORE_Handle *h = cls; | ||
243 | |||
244 | LOG (GNUNET_ERROR_TYPE_DEBUG, "MQ ERROR: %d\n", error); | ||
245 | reconnect_later (h); | ||
246 | } | ||
247 | |||
248 | |||
249 | /** | ||
250 | * Implement sending functionality of a message queue for | ||
251 | * us sending messages to a peer. | ||
252 | * | ||
253 | * @param mq the message queue | ||
254 | * @param msg the message to send | ||
255 | * @param impl_state state of the implementation | ||
256 | */ | ||
257 | static void | ||
258 | core_mq_send_impl (struct GNUNET_MQ_Handle *mq, | ||
259 | const struct GNUNET_MessageHeader *msg, | ||
260 | void *impl_state) | ||
261 | { | ||
262 | struct PeerRecord *pr = impl_state; | ||
263 | struct GNUNET_CORE_Handle *h = pr->h; | ||
264 | struct SendMessageRequest *smr; | ||
265 | struct SendMessage *sm; | ||
266 | struct GNUNET_MQ_Envelope *env; | ||
267 | uint16_t msize; | ||
268 | enum GNUNET_MQ_PriorityPreferences flags; | ||
269 | |||
270 | if (NULL == h->mq) | ||
271 | { | ||
272 | /* We're currently reconnecting, pretend this worked */ | ||
273 | GNUNET_MQ_impl_send_continue (mq); | ||
274 | return; | ||
275 | } | ||
276 | GNUNET_assert (NULL == pr->env); | ||
277 | /* extract options from envelope */ | ||
278 | env = GNUNET_MQ_get_current_envelope (mq); | ||
279 | flags = GNUNET_MQ_env_get_options (env); | ||
280 | |||
281 | /* check message size for sanity */ | ||
282 | msize = ntohs (msg->size); | ||
283 | if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof(struct SendMessage)) | ||
284 | { | ||
285 | GNUNET_break (0); | ||
286 | GNUNET_MQ_impl_send_continue (mq); | ||
287 | return; | ||
288 | } | ||
289 | |||
290 | /* ask core for transmission */ | ||
291 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
292 | "Asking core for transmission of %u bytes to `%s'\n", | ||
293 | (unsigned int) msize, | ||
294 | GNUNET_i2s (&pr->peer)); | ||
295 | env = GNUNET_MQ_msg (smr, GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST); | ||
296 | smr->priority = htonl ((uint32_t) flags); | ||
297 | smr->peer = pr->peer; | ||
298 | smr->size = htons (msize); | ||
299 | smr->smr_id = htons (++pr->smr_id_gen); | ||
300 | GNUNET_MQ_send (h->mq, env); | ||
301 | |||
302 | /* prepare message with actual transmission data */ | ||
303 | pr->env = GNUNET_MQ_msg_nested_mh (sm, GNUNET_MESSAGE_TYPE_CORE_SEND, msg); | ||
304 | sm->priority = htonl ((uint32_t) flags); | ||
305 | sm->peer = pr->peer; | ||
306 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
307 | "Calling get_message with buffer of %u bytes\n", | ||
308 | (unsigned int) msize); | ||
309 | } | ||
310 | |||
311 | |||
312 | /** | ||
313 | * Handle destruction of a message queue. Implementations must not | ||
314 | * free @a mq, but should take care of @a impl_state. | ||
315 | * | ||
316 | * @param mq the message queue to destroy | ||
317 | * @param impl_state state of the implementation | ||
318 | */ | ||
319 | static void | ||
320 | core_mq_destroy_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) | ||
321 | { | ||
322 | struct PeerRecord *pr = impl_state; | ||
323 | |||
324 | GNUNET_assert (mq == pr->mq); | ||
325 | pr->mq = NULL; | ||
326 | } | ||
327 | |||
328 | |||
329 | /** | ||
330 | * Implementation function that cancels the currently sent message. | ||
331 | * Should basically undo whatever #mq_send_impl() did. | ||
332 | * | ||
333 | * @param mq message queue | ||
334 | * @param impl_state state specific to the implementation | ||
335 | */ | ||
336 | static void | ||
337 | core_mq_cancel_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) | ||
338 | { | ||
339 | struct PeerRecord *pr = impl_state; | ||
340 | |||
341 | (void) mq; | ||
342 | GNUNET_assert (NULL != pr->env); | ||
343 | GNUNET_MQ_discard (pr->env); | ||
344 | pr->env = NULL; | ||
345 | } | ||
346 | |||
347 | |||
348 | /** | ||
349 | * We had an error processing a message we forwarded from a peer to | ||
350 | * the CORE service. We should just complain about it but otherwise | ||
351 | * continue processing. | ||
352 | * | ||
353 | * @param cls closure | ||
354 | * @param error error code | ||
355 | */ | ||
356 | static void | ||
357 | core_mq_error_handler (void *cls, enum GNUNET_MQ_Error error) | ||
358 | { | ||
359 | /* struct PeerRecord *pr = cls; */ | ||
360 | (void) cls; | ||
361 | (void) error; | ||
362 | GNUNET_break_op (0); | ||
363 | } | ||
364 | |||
365 | |||
366 | /** | ||
367 | * Add the given peer to the list of our connected peers | ||
368 | * and create the respective data structures and notify | ||
369 | * the application. | ||
370 | * | ||
371 | * @param h the core handle | ||
372 | * @param peer the peer that is connecting to us | ||
373 | */ | ||
374 | static void | ||
375 | connect_peer (struct GNUNET_CORE_Handle *h, | ||
376 | const struct GNUNET_PeerIdentity *peer) | ||
377 | { | ||
378 | struct PeerRecord *pr; | ||
379 | |||
380 | pr = GNUNET_new (struct PeerRecord); | ||
381 | pr->peer = *peer; | ||
382 | pr->h = h; | ||
383 | GNUNET_assert (GNUNET_YES == | ||
384 | GNUNET_CONTAINER_multipeermap_put ( | ||
385 | h->peers, | ||
386 | &pr->peer, | ||
387 | pr, | ||
388 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
389 | pr->mq = GNUNET_MQ_queue_for_callbacks (&core_mq_send_impl, | ||
390 | &core_mq_destroy_impl, | ||
391 | &core_mq_cancel_impl, | ||
392 | pr, | ||
393 | h->handlers, | ||
394 | &core_mq_error_handler, | ||
395 | pr); | ||
396 | if (NULL != h->connects) | ||
397 | { | ||
398 | pr->client_cls = h->connects (h->cls, &pr->peer, pr->mq); | ||
399 | GNUNET_MQ_set_handlers_closure (pr->mq, pr->client_cls); | ||
400 | } | ||
401 | } | ||
402 | |||
403 | |||
404 | /** | ||
405 | * Handle init reply message received from CORE service. Notify | ||
406 | * application that we are now connected to the CORE. Also fake | ||
407 | * loopback connection. | ||
408 | * | ||
409 | * @param cls the `struct GNUNET_CORE_Handle` | ||
410 | * @param m the init reply | ||
411 | */ | ||
412 | static void | ||
413 | handle_init_reply (void *cls, const struct InitReplyMessage *m) | ||
414 | { | ||
415 | struct GNUNET_CORE_Handle *h = cls; | ||
416 | GNUNET_CORE_StartupCallback init; | ||
417 | |||
418 | GNUNET_break (0 == ntohl (m->reserved)); | ||
419 | h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS; | ||
420 | if (NULL != (init = h->init)) | ||
421 | { | ||
422 | /* mark so we don't call init on reconnect */ | ||
423 | h->init = NULL; | ||
424 | h->me = m->my_identity; | ||
425 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
426 | "Connected to core service of peer `%s'.\n", | ||
427 | GNUNET_i2s (&h->me)); | ||
428 | h->have_init = GNUNET_YES; | ||
429 | init (h->cls, &h->me); | ||
430 | } | ||
431 | else | ||
432 | { | ||
433 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
434 | "Successfully reconnected to core service.\n"); | ||
435 | if (GNUNET_NO == h->have_init) | ||
436 | { | ||
437 | h->me = m->my_identity; | ||
438 | h->have_init = GNUNET_YES; | ||
439 | } | ||
440 | else | ||
441 | { | ||
442 | GNUNET_break (0 == memcmp (&h->me, | ||
443 | &m->my_identity, | ||
444 | sizeof(struct GNUNET_PeerIdentity))); | ||
445 | } | ||
446 | } | ||
447 | /* fake 'connect to self' */ | ||
448 | connect_peer (h, &h->me); | ||
449 | } | ||
450 | |||
451 | |||
452 | /** | ||
453 | * Handle connect message received from CORE service. | ||
454 | * Notify the application about the new connection. | ||
455 | * | ||
456 | * @param cls the `struct GNUNET_CORE_Handle` | ||
457 | * @param cnm the connect message | ||
458 | */ | ||
459 | static void | ||
460 | handle_connect_notify (void *cls, const struct ConnectNotifyMessage *cnm) | ||
461 | { | ||
462 | struct GNUNET_CORE_Handle *h = cls; | ||
463 | struct PeerRecord *pr; | ||
464 | |||
465 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
466 | "Received notification about connection from `%s'.\n", | ||
467 | GNUNET_i2s (&cnm->peer)); | ||
468 | if (0 == memcmp (&h->me, &cnm->peer, sizeof(struct GNUNET_PeerIdentity))) | ||
469 | { | ||
470 | /* connect to self!? */ | ||
471 | GNUNET_break (0); | ||
472 | return; | ||
473 | } | ||
474 | pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &cnm->peer); | ||
475 | if (NULL != pr) | ||
476 | { | ||
477 | GNUNET_break (0); | ||
478 | reconnect_later (h); | ||
479 | return; | ||
480 | } | ||
481 | connect_peer (h, &cnm->peer); | ||
482 | } | ||
483 | |||
484 | |||
485 | /** | ||
486 | * Handle disconnect message received from CORE service. | ||
487 | * Notify the application about the lost connection. | ||
488 | * | ||
489 | * @param cls the `struct GNUNET_CORE_Handle` | ||
490 | * @param dnm message about the disconnect event | ||
491 | */ | ||
492 | static void | ||
493 | handle_disconnect_notify (void *cls, const struct DisconnectNotifyMessage *dnm) | ||
494 | { | ||
495 | struct GNUNET_CORE_Handle *h = cls; | ||
496 | struct PeerRecord *pr; | ||
497 | |||
498 | if (0 == memcmp (&h->me, &dnm->peer, sizeof(struct GNUNET_PeerIdentity))) | ||
499 | { | ||
500 | /* disconnect from self!? */ | ||
501 | GNUNET_break (0); | ||
502 | return; | ||
503 | } | ||
504 | GNUNET_break (0 == ntohl (dnm->reserved)); | ||
505 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
506 | "Received notification about disconnect from `%s'.\n", | ||
507 | GNUNET_i2s (&dnm->peer)); | ||
508 | pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &dnm->peer); | ||
509 | if (NULL == pr) | ||
510 | { | ||
511 | GNUNET_break (0); | ||
512 | reconnect_later (h); | ||
513 | return; | ||
514 | } | ||
515 | disconnect_and_free_peer_entry (h, &pr->peer, pr); | ||
516 | } | ||
517 | |||
518 | |||
519 | /** | ||
520 | * Check that message received from CORE service is well-formed. | ||
521 | * | ||
522 | * @param cls the `struct GNUNET_CORE_Handle` | ||
523 | * @param ntm the message we got | ||
524 | * @return #GNUNET_OK if the message is well-formed | ||
525 | */ | ||
526 | static int | ||
527 | check_notify_inbound (void *cls, const struct NotifyTrafficMessage *ntm) | ||
528 | { | ||
529 | uint16_t msize; | ||
530 | const struct GNUNET_MessageHeader *em; | ||
531 | |||
532 | (void) cls; | ||
533 | msize = ntohs (ntm->header.size) - sizeof(struct NotifyTrafficMessage); | ||
534 | if (msize < sizeof(struct GNUNET_MessageHeader)) | ||
535 | { | ||
536 | GNUNET_break (0); | ||
537 | return GNUNET_SYSERR; | ||
538 | } | ||
539 | em = (const struct GNUNET_MessageHeader *) &ntm[1]; | ||
540 | if (msize != ntohs (em->size)) | ||
541 | { | ||
542 | GNUNET_break (0); | ||
543 | return GNUNET_SYSERR; | ||
544 | } | ||
545 | return GNUNET_OK; | ||
546 | } | ||
547 | |||
548 | |||
549 | /** | ||
550 | * Handle inbound message received from CORE service. If applicable, | ||
551 | * notify the application. | ||
552 | * | ||
553 | * @param cls the `struct GNUNET_CORE_Handle` | ||
554 | * @param ntm the message we got from CORE. | ||
555 | */ | ||
556 | static void | ||
557 | handle_notify_inbound (void *cls, const struct NotifyTrafficMessage *ntm) | ||
558 | { | ||
559 | struct GNUNET_CORE_Handle *h = cls; | ||
560 | const struct GNUNET_MessageHeader *em; | ||
561 | struct PeerRecord *pr; | ||
562 | |||
563 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
564 | "Received inbound message from `%s'.\n", | ||
565 | GNUNET_i2s (&ntm->peer)); | ||
566 | em = (const struct GNUNET_MessageHeader *) &ntm[1]; | ||
567 | pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &ntm->peer); | ||
568 | if (NULL == pr) | ||
569 | { | ||
570 | GNUNET_break (0); | ||
571 | reconnect_later (h); | ||
572 | return; | ||
573 | } | ||
574 | GNUNET_MQ_inject_message (pr->mq, em); | ||
575 | } | ||
576 | |||
577 | |||
578 | /** | ||
579 | * Handle message received from CORE service notifying us that we are | ||
580 | * now allowed to send a message to a peer. If that message is still | ||
581 | * pending, put it into the queue to be transmitted. | ||
582 | * | ||
583 | * @param cls the `struct GNUNET_CORE_Handle` | ||
584 | * @param smr the message we got | ||
585 | */ | ||
586 | static void | ||
587 | handle_send_ready (void *cls, const struct SendMessageReady *smr) | ||
588 | { | ||
589 | struct GNUNET_CORE_Handle *h = cls; | ||
590 | struct PeerRecord *pr; | ||
591 | |||
592 | pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &smr->peer); | ||
593 | if (NULL == pr) | ||
594 | { | ||
595 | GNUNET_break (0); | ||
596 | reconnect_later (h); | ||
597 | return; | ||
598 | } | ||
599 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
600 | "Received notification about transmission readiness to `%s'.\n", | ||
601 | GNUNET_i2s (&smr->peer)); | ||
602 | if (NULL == pr->env) | ||
603 | { | ||
604 | /* request must have been cancelled between the original request | ||
605 | * and the response from CORE, ignore CORE's readiness */ | ||
606 | return; | ||
607 | } | ||
608 | if (ntohs (smr->smr_id) != pr->smr_id_gen) | ||
609 | { | ||
610 | /* READY message is for expired or cancelled message, | ||
611 | * ignore! (we should have already sent another request) */ | ||
612 | return; | ||
613 | } | ||
614 | |||
615 | /* ok, all good, send message out! */ | ||
616 | GNUNET_MQ_send (h->mq, pr->env); | ||
617 | pr->env = NULL; | ||
618 | GNUNET_MQ_impl_send_continue (pr->mq); | ||
619 | } | ||
620 | |||
621 | |||
622 | /** | ||
623 | * Our current client connection went down. Clean it up and try to | ||
624 | * reconnect! | ||
625 | * | ||
626 | * @param h our handle to the core service | ||
627 | */ | ||
628 | static void | ||
629 | reconnect (struct GNUNET_CORE_Handle *h) | ||
630 | { | ||
631 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
632 | { GNUNET_MQ_hd_fixed_size (init_reply, | ||
633 | GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY, | ||
634 | struct InitReplyMessage, | ||
635 | h), | ||
636 | GNUNET_MQ_hd_fixed_size (connect_notify, | ||
637 | GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT, | ||
638 | struct ConnectNotifyMessage, | ||
639 | h), | ||
640 | GNUNET_MQ_hd_fixed_size (disconnect_notify, | ||
641 | GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT, | ||
642 | struct DisconnectNotifyMessage, | ||
643 | h), | ||
644 | GNUNET_MQ_hd_var_size (notify_inbound, | ||
645 | GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND, | ||
646 | struct NotifyTrafficMessage, | ||
647 | h), | ||
648 | GNUNET_MQ_hd_fixed_size (send_ready, | ||
649 | GNUNET_MESSAGE_TYPE_CORE_SEND_READY, | ||
650 | struct SendMessageReady, | ||
651 | h), | ||
652 | GNUNET_MQ_handler_end () }; | ||
653 | struct InitMessage *init; | ||
654 | struct GNUNET_MQ_Envelope *env; | ||
655 | uint16_t *ts; | ||
656 | |||
657 | GNUNET_assert (NULL == h->mq); | ||
658 | h->mq = GNUNET_CLIENT_connect (h->cfg, "core", handlers, &handle_mq_error, h); | ||
659 | if (NULL == h->mq) | ||
660 | { | ||
661 | reconnect_later (h); | ||
662 | return; | ||
663 | } | ||
664 | env = GNUNET_MQ_msg_extra (init, | ||
665 | sizeof(uint16_t) * h->hcnt, | ||
666 | GNUNET_MESSAGE_TYPE_CORE_INIT); | ||
667 | LOG (GNUNET_ERROR_TYPE_INFO, "(Re)connecting to CORE service\n"); | ||
668 | init->options = htonl (0); | ||
669 | ts = (uint16_t *) &init[1]; | ||
670 | for (unsigned int hpos = 0; hpos < h->hcnt; hpos++) | ||
671 | ts[hpos] = htons (h->handlers[hpos].type); | ||
672 | GNUNET_MQ_send (h->mq, env); | ||
673 | } | ||
674 | |||
675 | |||
676 | /** | ||
677 | * Connect to the core service. Note that the connection may complete | ||
678 | * (or fail) asynchronously. | ||
679 | * | ||
680 | * @param cfg configuration to use | ||
681 | * @param cls closure for the various callbacks that follow (including handlers in the handlers array) | ||
682 | * @param init callback to call once we have successfully | ||
683 | * connected to the core service | ||
684 | * @param connects function to call on peer connect, can be NULL | ||
685 | * @param disconnects function to call on peer disconnect / timeout, can be NULL | ||
686 | * @param handlers callbacks for messages we care about, NULL-terminated | ||
687 | * @return handle to the core service (only useful for disconnect until @a init is called); | ||
688 | * NULL on error (in this case, init is never called) | ||
689 | */ | ||
690 | struct GNUNET_CORE_Handle * | ||
691 | GNUNET_CORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
692 | void *cls, | ||
693 | GNUNET_CORE_StartupCallback init, | ||
694 | GNUNET_CORE_ConnectEventHandler connects, | ||
695 | GNUNET_CORE_DisconnectEventHandler disconnects, | ||
696 | const struct GNUNET_MQ_MessageHandler *handlers) | ||
697 | { | ||
698 | struct GNUNET_CORE_Handle *h; | ||
699 | |||
700 | h = GNUNET_new (struct GNUNET_CORE_Handle); | ||
701 | h->cfg = cfg; | ||
702 | h->cls = cls; | ||
703 | h->init = init; | ||
704 | h->connects = connects; | ||
705 | h->disconnects = disconnects; | ||
706 | h->peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO); | ||
707 | h->handlers = GNUNET_MQ_copy_handlers (handlers); | ||
708 | h->hcnt = GNUNET_MQ_count_handlers (handlers); | ||
709 | GNUNET_assert (h->hcnt < | ||
710 | (GNUNET_MAX_MESSAGE_SIZE - sizeof(struct InitMessage)) | ||
711 | / sizeof(uint16_t)); | ||
712 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service\n"); | ||
713 | reconnect (h); | ||
714 | if (NULL == h->mq) | ||
715 | { | ||
716 | GNUNET_CORE_disconnect (h); | ||
717 | return NULL; | ||
718 | } | ||
719 | return h; | ||
720 | } | ||
721 | |||
722 | |||
723 | /** | ||
724 | * Disconnect from the core service. | ||
725 | * | ||
726 | * @param handle connection to core to disconnect | ||
727 | */ | ||
728 | void | ||
729 | GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle) | ||
730 | { | ||
731 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from CORE service\n"); | ||
732 | GNUNET_CONTAINER_multipeermap_iterate (handle->peers, | ||
733 | &disconnect_and_free_peer_entry, | ||
734 | handle); | ||
735 | GNUNET_CONTAINER_multipeermap_destroy (handle->peers); | ||
736 | handle->peers = NULL; | ||
737 | if (NULL != handle->reconnect_task) | ||
738 | { | ||
739 | GNUNET_SCHEDULER_cancel (handle->reconnect_task); | ||
740 | handle->reconnect_task = NULL; | ||
741 | } | ||
742 | if (NULL != handle->mq) | ||
743 | { | ||
744 | GNUNET_MQ_destroy (handle->mq); | ||
745 | handle->mq = NULL; | ||
746 | } | ||
747 | GNUNET_free (handle->handlers); | ||
748 | GNUNET_free (handle); | ||
749 | } | ||
750 | |||
751 | |||
752 | /** | ||
753 | * Obtain the message queue for a connected peer. | ||
754 | * | ||
755 | * @param h the core handle | ||
756 | * @param pid the identity of the peer to check if it has been connected to us | ||
757 | * @return NULL if peer is not connected | ||
758 | */ | ||
759 | struct GNUNET_MQ_Handle * | ||
760 | GNUNET_CORE_get_mq (const struct GNUNET_CORE_Handle *h, | ||
761 | const struct GNUNET_PeerIdentity *pid) | ||
762 | { | ||
763 | struct PeerRecord *pr; | ||
764 | |||
765 | pr = GNUNET_CONTAINER_multipeermap_get (h->peers, pid); | ||
766 | if (NULL == pr) | ||
767 | return NULL; | ||
768 | return pr->mq; | ||
769 | } | ||
770 | |||
771 | |||
772 | /* end of core_api.c */ | ||
diff --git a/src/service/core/core_api_cmd_connecting_peers.c b/src/service/core/core_api_cmd_connecting_peers.c new file mode 100644 index 000000000..0d177a8e9 --- /dev/null +++ b/src/service/core/core_api_cmd_connecting_peers.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2021 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core_api_cmd_connecting_peers.c | ||
23 | * @brief cmd to start a peer. | ||
24 | * @author t3sserakt | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_core_testing_lib.h" | ||
29 | #include "gnunet_transport_testing_ng_lib.h" | ||
30 | #include "gnunet_transport_application_service.h" | ||
31 | #include "gnunet_transport_core_service.h" | ||
32 | |||
33 | /** | ||
34 | * Generic logging shortcut | ||
35 | */ | ||
36 | #define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) | ||
37 | |||
38 | |||
39 | /** | ||
40 | * The run method of this cmd will connect to peers. | ||
41 | * | ||
42 | */ | ||
43 | static void | ||
44 | connect_peers_run (void *cls, | ||
45 | struct GNUNET_TESTING_Interpreter *is) | ||
46 | { | ||
47 | struct GNUNET_TESTING_ConnectPeersState *cps = cls; | ||
48 | const struct GNUNET_TESTING_StartPeerState *sps; | ||
49 | const struct GNUNET_TESTING_Command *system_cmd; | ||
50 | const struct GNUNET_TESTING_System *tl_system; | ||
51 | const struct GNUNET_TESTING_Command *peer1_cmd; | ||
52 | struct GNUNET_PeerIdentity *peer; | ||
53 | enum GNUNET_NetworkType nt = 0; | ||
54 | struct GNUNET_TESTING_NodeConnection *pos_connection; | ||
55 | struct GNUNET_TESTING_AddressPrefix *pos_prefix; | ||
56 | const enum GNUNET_GenericReturnValue *broadcast; | ||
57 | unsigned int con_num = 0; | ||
58 | uint32_t num; | ||
59 | char *addr; | ||
60 | char *addr_and_port; | ||
61 | char *emsg = NULL; | ||
62 | |||
63 | cps->is = is; | ||
64 | peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, | ||
65 | cps->start_peer_label); | ||
66 | GNUNET_TRANSPORT_TESTING_get_trait_broadcast (peer1_cmd, | ||
67 | &broadcast); | ||
68 | GNUNET_TRANSPORT_TESTING_get_trait_state (peer1_cmd, | ||
69 | &sps); | ||
70 | |||
71 | system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, | ||
72 | cps->create_label); | ||
73 | GNUNET_TESTING_get_trait_test_system (system_cmd, | ||
74 | &tl_system); | ||
75 | |||
76 | cps->tl_system = tl_system; | ||
77 | |||
78 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
79 | "cps->num: %u \n", | ||
80 | cps->num); | ||
81 | |||
82 | |||
83 | cps->ah = GNUNET_TRANSPORT_application_init (sps->cfg); | ||
84 | if (NULL == cps->ah) | ||
85 | { | ||
86 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
87 | "Failed to initialize the TRANSPORT application suggestion client handle for peer `%s': `%s'\n", | ||
88 | sps->cfgname, | ||
89 | emsg); | ||
90 | GNUNET_free (emsg); | ||
91 | GNUNET_TESTING_interpreter_fail (is); | ||
92 | return; | ||
93 | } | ||
94 | |||
95 | cps->node_connections_head = GNUNET_TESTING_get_connections (cps->num, | ||
96 | cps->topology); | ||
97 | |||
98 | for (pos_connection = cps->node_connections_head; NULL != pos_connection; | ||
99 | pos_connection = pos_connection->next) | ||
100 | { | ||
101 | con_num++; | ||
102 | num = GNUNET_TESTING_calculate_num (pos_connection, cps->topology); | ||
103 | for (pos_prefix = pos_connection->address_prefixes_head; NULL != pos_prefix; | ||
104 | pos_prefix = | ||
105 | pos_prefix->next) | ||
106 | { | ||
107 | addr = GNUNET_TESTING_get_address (pos_connection, | ||
108 | pos_prefix->address_prefix); | ||
109 | if (NULL != addr) | ||
110 | { | ||
111 | char *natted_p = strstr (pos_prefix->address_prefix, "_"); | ||
112 | |||
113 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
114 | "0 validating peer number %s %s %s\n", | ||
115 | natted_p, | ||
116 | pos_prefix->address_prefix, | ||
117 | addr); | ||
118 | if (0 == GNUNET_memcmp (pos_prefix->address_prefix, "udp")) | ||
119 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
120 | "validating memcmp\n"); | ||
121 | if (GNUNET_YES == *broadcast) | ||
122 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
123 | "validating broadcast\n"); | ||
124 | if ((0 == GNUNET_memcmp (pos_prefix->address_prefix, "udp")) && | ||
125 | (GNUNET_YES == *broadcast) ) | ||
126 | GNUNET_asprintf (&addr_and_port, | ||
127 | "%s:2086", | ||
128 | addr); | ||
129 | else if (NULL == natted_p) | ||
130 | GNUNET_asprintf (&addr_and_port, | ||
131 | "%s:60002", | ||
132 | addr); | ||
133 | else if (NULL != natted_p) | ||
134 | { | ||
135 | char *prefix; | ||
136 | char *rest; | ||
137 | char *address; | ||
138 | |||
139 | prefix = strtok (addr, "_"); | ||
140 | rest = strtok (NULL, "_"); | ||
141 | strtok (rest, "-"); | ||
142 | address = strtok (NULL, "-"); | ||
143 | |||
144 | GNUNET_asprintf (&addr_and_port, | ||
145 | "%s-%s:0", | ||
146 | prefix, | ||
147 | address); | ||
148 | |||
149 | } | ||
150 | peer = GNUNET_TESTING_get_peer (num, tl_system); | ||
151 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
152 | "validating peer number %u with identity %s and address %s %u %s and handle %p\n", | ||
153 | num, | ||
154 | GNUNET_i2s (peer), | ||
155 | addr_and_port, | ||
156 | *broadcast, | ||
157 | pos_prefix->address_prefix, | ||
158 | cps->ah); | ||
159 | GNUNET_TRANSPORT_application_validate ((struct | ||
160 | GNUNET_TRANSPORT_ApplicationHandle | ||
161 | *) cps->ah, | ||
162 | peer, | ||
163 | nt, | ||
164 | addr_and_port); | ||
165 | GNUNET_free (peer); | ||
166 | GNUNET_free (addr); | ||
167 | GNUNET_free (addr_and_port); | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | cps->con_num = con_num; | ||
172 | } | ||
173 | |||
174 | |||
175 | /** | ||
176 | * The cleanup function of this cmd frees resources the cmd allocated. | ||
177 | * | ||
178 | */ | ||
179 | static void | ||
180 | connect_peers_cleanup (void *cls) | ||
181 | { | ||
182 | struct GNUNET_TESTING_ConnectPeersState *cps = cls; | ||
183 | |||
184 | GNUNET_free (cps->connected_peers_map); | ||
185 | GNUNET_free (cps); | ||
186 | } | ||
187 | |||
188 | |||
189 | /** | ||
190 | * This function prepares an array with traits. | ||
191 | * | ||
192 | */ | ||
193 | enum GNUNET_GenericReturnValue | ||
194 | connect_peers_traits (void *cls, | ||
195 | const void **ret, | ||
196 | const char *trait, | ||
197 | unsigned int index) | ||
198 | { | ||
199 | struct GNUNET_TESTING_ConnectPeersState *cps = cls; | ||
200 | struct GNUNET_TESTING_Trait traits[] = { | ||
201 | GNUNET_CORE_TESTING_make_trait_connect_peer_state ((const void *) cps), | ||
202 | GNUNET_TESTING_trait_end () | ||
203 | }; | ||
204 | return GNUNET_TESTING_get_trait (traits, | ||
205 | ret, | ||
206 | trait, | ||
207 | index); | ||
208 | } | ||
209 | |||
210 | |||
211 | struct GNUNET_TESTING_Command | ||
212 | GNUNET_CORE_cmd_connect_peers ( | ||
213 | const char *label, | ||
214 | const char *start_peer_label, | ||
215 | const char *create_label, | ||
216 | uint32_t num, | ||
217 | struct GNUNET_TESTING_NetjailTopology *topology, | ||
218 | unsigned int additional_connects, | ||
219 | unsigned int wait_for_connect, | ||
220 | struct GNUNET_MQ_MessageHandler *handlers) | ||
221 | { | ||
222 | struct GNUNET_TESTING_ConnectPeersState *cps; | ||
223 | unsigned int node_additional_connects; | ||
224 | struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map = | ||
225 | GNUNET_CONTAINER_multishortmap_create (1,GNUNET_NO); | ||
226 | unsigned int i; | ||
227 | |||
228 | node_additional_connects = GNUNET_TESTING_get_additional_connects (num, | ||
229 | topology); | ||
230 | |||
231 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
232 | "global: %u and local: %u additional_connects\n", | ||
233 | additional_connects, | ||
234 | node_additional_connects); | ||
235 | |||
236 | if (0 != node_additional_connects) | ||
237 | additional_connects = node_additional_connects; | ||
238 | |||
239 | cps = GNUNET_new (struct GNUNET_TESTING_ConnectPeersState); | ||
240 | cps->start_peer_label = start_peer_label; | ||
241 | cps->num = num; | ||
242 | cps->create_label = create_label; | ||
243 | cps->topology = topology; | ||
244 | cps->additional_connects = additional_connects; | ||
245 | cps->wait_for_connect = wait_for_connect; | ||
246 | cps->connected_peers_map = connected_peers_map; | ||
247 | |||
248 | if (NULL != handlers) | ||
249 | { | ||
250 | for (i = 0; NULL != handlers[i].cb; i++) | ||
251 | ; | ||
252 | cps->handlers = GNUNET_new_array (i + 1, | ||
253 | struct GNUNET_MQ_MessageHandler); | ||
254 | GNUNET_memcpy (cps->handlers, | ||
255 | handlers, | ||
256 | i * sizeof(struct GNUNET_MQ_MessageHandler)); | ||
257 | } | ||
258 | // FIXME: wrap with cmd_make_unblocking! | ||
259 | if (GNUNET_YES == wait_for_connect) | ||
260 | return GNUNET_TESTING_command_new_ac (cps, | ||
261 | label, | ||
262 | &connect_peers_run, | ||
263 | &connect_peers_cleanup, | ||
264 | &connect_peers_traits, | ||
265 | &cps->ac); | ||
266 | else | ||
267 | return GNUNET_TESTING_command_new (cps, | ||
268 | label, | ||
269 | &connect_peers_run, | ||
270 | &connect_peers_cleanup, | ||
271 | &connect_peers_traits); | ||
272 | } | ||
273 | |||
274 | |||
275 | // FIXME: likely not ideally placed here, move to its own file | ||
276 | GNUNET_CORE_TESTING_SIMPLE_TRAITS (GNUNET_TESTING_MAKE_IMPL_SIMPLE_TRAIT, | ||
277 | GNUNET_CORE_TESTING) | ||
diff --git a/src/service/core/core_api_monitor_peers.c b/src/service/core/core_api_monitor_peers.c new file mode 100644 index 000000000..3be8e3859 --- /dev/null +++ b/src/service/core/core_api_monitor_peers.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009-2014, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/core_api_monitor_peers.c | ||
23 | * @brief implementation of the peer_iterate function | ||
24 | * @author Christian Grothoff | ||
25 | * @author Nathan Evans | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_core_service.h" | ||
29 | #include "core.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Handle to a CORE monitoring operation. | ||
34 | */ | ||
35 | struct GNUNET_CORE_MonitorHandle | ||
36 | { | ||
37 | /** | ||
38 | * Our configuration. | ||
39 | */ | ||
40 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
41 | |||
42 | /** | ||
43 | * Our connection to the service. | ||
44 | */ | ||
45 | struct GNUNET_MQ_Handle *mq; | ||
46 | |||
47 | /** | ||
48 | * Function called with the peer. | ||
49 | */ | ||
50 | GNUNET_CORE_MonitorCallback peer_cb; | ||
51 | |||
52 | /** | ||
53 | * Closure for @e peer_cb. | ||
54 | */ | ||
55 | void *peer_cb_cls; | ||
56 | }; | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Protocol error, reconnect to CORE service and notify | ||
61 | * client. | ||
62 | * | ||
63 | * @param mh monitoring session to reconnect to CORE | ||
64 | */ | ||
65 | static void | ||
66 | reconnect (struct GNUNET_CORE_MonitorHandle *mh); | ||
67 | |||
68 | |||
69 | /** | ||
70 | * Generic error handler, called with the appropriate error code and | ||
71 | * the same closure specified at the creation of the message queue. | ||
72 | * Not every message queue implementation supports an error handler. | ||
73 | * | ||
74 | * @param cls closure, a `struct GNUNET_CORE_MonitorHandle *` | ||
75 | * @param error error code | ||
76 | */ | ||
77 | static void | ||
78 | handle_mq_error (void *cls, enum GNUNET_MQ_Error error) | ||
79 | { | ||
80 | struct GNUNET_CORE_MonitorHandle *mh = cls; | ||
81 | |||
82 | (void) error; | ||
83 | reconnect (mh); | ||
84 | } | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Receive reply from CORE service with information about a peer. | ||
89 | * | ||
90 | * @param cls our `struct GNUNET_CORE_MonitorHandle *` | ||
91 | * @param mon_message monitor message | ||
92 | */ | ||
93 | static void | ||
94 | handle_receive_info (void *cls, const struct MonitorNotifyMessage *mon_message) | ||
95 | { | ||
96 | struct GNUNET_CORE_MonitorHandle *mh = cls; | ||
97 | |||
98 | mh->peer_cb (mh->peer_cb_cls, | ||
99 | &mon_message->peer, | ||
100 | (enum GNUNET_CORE_KxState) ntohl (mon_message->state), | ||
101 | GNUNET_TIME_absolute_ntoh (mon_message->timeout)); | ||
102 | } | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Protocol error, reconnect to CORE service and notify | ||
107 | * client. | ||
108 | * | ||
109 | * @param mh monitoring session to reconnect to CORE | ||
110 | */ | ||
111 | static void | ||
112 | reconnect (struct GNUNET_CORE_MonitorHandle *mh) | ||
113 | { | ||
114 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
115 | { GNUNET_MQ_hd_fixed_size (receive_info, | ||
116 | GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY, | ||
117 | struct MonitorNotifyMessage, | ||
118 | mh), | ||
119 | GNUNET_MQ_handler_end () }; | ||
120 | struct GNUNET_MQ_Envelope *env; | ||
121 | struct GNUNET_MessageHeader *msg; | ||
122 | |||
123 | if (NULL != mh->mq) | ||
124 | GNUNET_MQ_destroy (mh->mq); | ||
125 | /* FIXME: use backoff? */ | ||
126 | mh->mq = | ||
127 | GNUNET_CLIENT_connect (mh->cfg, "core", handlers, &handle_mq_error, mh); | ||
128 | if (NULL == mh->mq) | ||
129 | return; | ||
130 | /* notify callback about reconnect */ | ||
131 | if (NULL != mh->peer_cb) | ||
132 | mh->peer_cb (mh->peer_cb_cls, | ||
133 | NULL, | ||
134 | GNUNET_CORE_KX_CORE_DISCONNECT, | ||
135 | GNUNET_TIME_UNIT_FOREVER_ABS); | ||
136 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS); | ||
137 | GNUNET_MQ_send (mh->mq, env); | ||
138 | } | ||
139 | |||
140 | |||
141 | /** | ||
142 | * Monitor connectivity and KX status of all peers known to CORE. | ||
143 | * Calls @a peer_cb with the current status for each connected peer, | ||
144 | * and then once with NULL to indicate that all peers that are | ||
145 | * currently active have been handled. After that, the iteration | ||
146 | * continues until it is cancelled. Normal users of the CORE API are | ||
147 | * not expected to use this function. It is different in that it | ||
148 | * truly lists all connections (including those where the KX is in | ||
149 | * progress), not just those relevant to the application. This | ||
150 | * function is used by special applications for diagnostics. | ||
151 | * | ||
152 | * @param cfg configuration handle | ||
153 | * @param peer_cb function to call with the peer information | ||
154 | * @param peer_cb_cls closure for @a peer_cb | ||
155 | * @return NULL on error | ||
156 | */ | ||
157 | struct GNUNET_CORE_MonitorHandle * | ||
158 | GNUNET_CORE_monitor_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
159 | GNUNET_CORE_MonitorCallback peer_cb, | ||
160 | void *peer_cb_cls) | ||
161 | { | ||
162 | struct GNUNET_CORE_MonitorHandle *mh; | ||
163 | |||
164 | GNUNET_assert (NULL != peer_cb); | ||
165 | mh = GNUNET_new (struct GNUNET_CORE_MonitorHandle); | ||
166 | mh->cfg = cfg; | ||
167 | reconnect (mh); | ||
168 | mh->peer_cb = peer_cb; | ||
169 | mh->peer_cb_cls = peer_cb_cls; | ||
170 | if (NULL == mh->mq) | ||
171 | { | ||
172 | GNUNET_free (mh); | ||
173 | return NULL; | ||
174 | } | ||
175 | return mh; | ||
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * Stop monitoring CORE activity. | ||
181 | * | ||
182 | * @param mh monitor to stop | ||
183 | */ | ||
184 | void | ||
185 | GNUNET_CORE_monitor_stop (struct GNUNET_CORE_MonitorHandle *mh) | ||
186 | { | ||
187 | if (NULL != mh->mq) | ||
188 | { | ||
189 | GNUNET_MQ_destroy (mh->mq); | ||
190 | mh->mq = NULL; | ||
191 | } | ||
192 | GNUNET_free (mh); | ||
193 | } | ||
194 | |||
195 | |||
196 | /* end of core_api_monitor_peers.c */ | ||
diff --git a/src/service/core/gnunet-service-core.c b/src/service/core/gnunet-service-core.c new file mode 100644 index 000000000..e387fecc9 --- /dev/null +++ b/src/service/core/gnunet-service-core.c | |||
@@ -0,0 +1,988 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core.c | ||
23 | * @brief high-level P2P messaging | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include <gcrypt.h> | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet-service-core.h" | ||
30 | #include "gnunet-service-core_kx.h" | ||
31 | #include "gnunet-service-core_sessions.h" | ||
32 | #include "gnunet-service-core_typemap.h" | ||
33 | #include "gnunet_constants.h" | ||
34 | |||
35 | /** | ||
36 | * How many messages do we queue up at most for any client? This can | ||
37 | * cause messages to be dropped if clients do not process them fast | ||
38 | * enough! Note that this is a soft limit; we try | ||
39 | * to keep a few larger messages above the limit. | ||
40 | */ | ||
41 | #define SOFT_MAX_QUEUE 128 | ||
42 | |||
43 | /** | ||
44 | * How many messages do we queue up at most for any client? This can | ||
45 | * cause messages to be dropped if clients do not process them fast | ||
46 | * enough! Note that this is the hard limit. | ||
47 | */ | ||
48 | #define HARD_MAX_QUEUE 256 | ||
49 | |||
50 | |||
51 | /** | ||
52 | * Data structure for each client connected to the CORE service. | ||
53 | */ | ||
54 | struct GSC_Client | ||
55 | { | ||
56 | /** | ||
57 | * Clients are kept in a linked list. | ||
58 | */ | ||
59 | struct GSC_Client *next; | ||
60 | |||
61 | /** | ||
62 | * Clients are kept in a linked list. | ||
63 | */ | ||
64 | struct GSC_Client *prev; | ||
65 | |||
66 | /** | ||
67 | * Handle for the client with the server API. | ||
68 | */ | ||
69 | struct GNUNET_SERVICE_Client *client; | ||
70 | |||
71 | /** | ||
72 | * Message queue to talk to @e client. | ||
73 | */ | ||
74 | struct GNUNET_MQ_Handle *mq; | ||
75 | |||
76 | /** | ||
77 | * Array of the types of messages this peer cares | ||
78 | * about (with @e tcnt entries). Allocated as part | ||
79 | * of this client struct, do not free! | ||
80 | */ | ||
81 | uint16_t *types; | ||
82 | |||
83 | /** | ||
84 | * Map of peer identities to active transmission requests of this | ||
85 | * client to the peer (of type `struct GSC_ClientActiveRequest`). | ||
86 | */ | ||
87 | struct GNUNET_CONTAINER_MultiPeerMap *requests; | ||
88 | |||
89 | /** | ||
90 | * Map containing all peers that this client knows we're connected to. | ||
91 | */ | ||
92 | struct GNUNET_CONTAINER_MultiPeerMap *connectmap; | ||
93 | |||
94 | /** | ||
95 | * Options for messages this client cares about, | ||
96 | * see GNUNET_CORE_OPTION_ values. | ||
97 | */ | ||
98 | uint32_t options; | ||
99 | |||
100 | /** | ||
101 | * Have we gotten the #GNUNET_MESSAGE_TYPE_CORE_INIT message | ||
102 | * from this client already? | ||
103 | */ | ||
104 | int got_init; | ||
105 | |||
106 | /** | ||
107 | * Number of types of incoming messages this client | ||
108 | * specifically cares about. Size of the @e types array. | ||
109 | */ | ||
110 | unsigned int tcnt; | ||
111 | }; | ||
112 | |||
113 | |||
114 | /** | ||
115 | * Our identity. | ||
116 | */ | ||
117 | struct GNUNET_PeerIdentity GSC_my_identity; | ||
118 | |||
119 | /** | ||
120 | * Our configuration. | ||
121 | */ | ||
122 | const struct GNUNET_CONFIGURATION_Handle *GSC_cfg; | ||
123 | |||
124 | /** | ||
125 | * For creating statistics. | ||
126 | */ | ||
127 | struct GNUNET_STATISTICS_Handle *GSC_stats; | ||
128 | |||
129 | /** | ||
130 | * Big "or" of all client options. | ||
131 | */ | ||
132 | static uint32_t all_client_options; | ||
133 | |||
134 | /** | ||
135 | * Head of linked list of our clients. | ||
136 | */ | ||
137 | static struct GSC_Client *client_head; | ||
138 | |||
139 | /** | ||
140 | * Tail of linked list of our clients. | ||
141 | */ | ||
142 | static struct GSC_Client *client_tail; | ||
143 | |||
144 | |||
145 | /** | ||
146 | * Test if the client is interested in messages of the given type. | ||
147 | * | ||
148 | * @param type message type | ||
149 | * @param c client to test | ||
150 | * @return #GNUNET_YES if @a c is interested, #GNUNET_NO if not. | ||
151 | */ | ||
152 | static int | ||
153 | type_match (uint16_t type, struct GSC_Client *c) | ||
154 | { | ||
155 | if ((0 == c->tcnt) && (0 != c->options)) | ||
156 | return GNUNET_YES; /* peer without handlers and inbound/outbond | ||
157 | callbacks matches ALL */ | ||
158 | if (NULL == c->types) | ||
159 | return GNUNET_NO; | ||
160 | for (unsigned int i = 0; i < c->tcnt; i++) | ||
161 | if (type == c->types[i]) | ||
162 | return GNUNET_YES; | ||
163 | return GNUNET_NO; | ||
164 | } | ||
165 | |||
166 | |||
167 | /** | ||
168 | * Check #GNUNET_MESSAGE_TYPE_CORE_INIT request. | ||
169 | * | ||
170 | * @param cls client that sent #GNUNET_MESSAGE_TYPE_CORE_INIT | ||
171 | * @param im the `struct InitMessage` | ||
172 | * @return #GNUNET_OK if @a im is well-formed | ||
173 | */ | ||
174 | static int | ||
175 | check_client_init (void *cls, const struct InitMessage *im) | ||
176 | { | ||
177 | return GNUNET_OK; | ||
178 | } | ||
179 | |||
180 | |||
181 | /** | ||
182 | * Handle #GNUNET_MESSAGE_TYPE_CORE_INIT request. | ||
183 | * | ||
184 | * @param cls client that sent #GNUNET_MESSAGE_TYPE_CORE_INIT | ||
185 | * @param im the `struct InitMessage` | ||
186 | */ | ||
187 | static void | ||
188 | handle_client_init (void *cls, const struct InitMessage *im) | ||
189 | { | ||
190 | struct GSC_Client *c = cls; | ||
191 | struct GNUNET_MQ_Envelope *env; | ||
192 | struct InitReplyMessage *irm; | ||
193 | uint16_t msize; | ||
194 | const uint16_t *types; | ||
195 | |||
196 | /* check that we don't have an entry already */ | ||
197 | msize = ntohs (im->header.size) - sizeof(struct InitMessage); | ||
198 | types = (const uint16_t *) &im[1]; | ||
199 | c->tcnt = msize / sizeof(uint16_t); | ||
200 | c->options = ntohl (im->options); | ||
201 | c->got_init = GNUNET_YES; | ||
202 | all_client_options |= c->options; | ||
203 | c->types = GNUNET_malloc (msize); | ||
204 | GNUNET_assert (GNUNET_YES == | ||
205 | GNUNET_CONTAINER_multipeermap_put ( | ||
206 | c->connectmap, | ||
207 | &GSC_my_identity, | ||
208 | NULL, | ||
209 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
210 | for (unsigned int i = 0; i < c->tcnt; i++) | ||
211 | c->types[i] = ntohs (types[i]); | ||
212 | GSC_TYPEMAP_add (c->types, c->tcnt); | ||
213 | GNUNET_log ( | ||
214 | GNUNET_ERROR_TYPE_DEBUG, | ||
215 | "Client connecting to core service is interested in %u message types\n", | ||
216 | (unsigned int) c->tcnt); | ||
217 | /* send init reply message */ | ||
218 | env = GNUNET_MQ_msg (irm, GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY); | ||
219 | irm->reserved = htonl (0); | ||
220 | irm->my_identity = GSC_my_identity; | ||
221 | GNUNET_MQ_send (c->mq, env); | ||
222 | GSC_SESSIONS_notify_client_about_sessions (c); | ||
223 | GNUNET_SERVICE_client_continue (c->client); | ||
224 | } | ||
225 | |||
226 | |||
227 | /** | ||
228 | * We will never be ready to transmit the given message in (disconnect | ||
229 | * or invalid request). Frees resources associated with @a car. We | ||
230 | * don't explicitly tell the client, it'll learn with the disconnect | ||
231 | * (or violated the protocol). | ||
232 | * | ||
233 | * @param car request that now permanently failed; the | ||
234 | * responsibility for the handle is now returned | ||
235 | * to CLIENTS (SESSIONS is done with it). | ||
236 | * @param drop_client #GNUNET_YES if the client violated the protocol | ||
237 | * and we should thus drop the connection | ||
238 | */ | ||
239 | void | ||
240 | GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car, | ||
241 | int drop_client) | ||
242 | { | ||
243 | GNUNET_assert ( | ||
244 | GNUNET_YES == | ||
245 | GNUNET_CONTAINER_multipeermap_remove (car->client_handle->requests, | ||
246 | &car->target, | ||
247 | car)); | ||
248 | if (GNUNET_YES == drop_client) | ||
249 | GNUNET_SERVICE_client_drop (car->client_handle->client); | ||
250 | GNUNET_free (car); | ||
251 | } | ||
252 | |||
253 | |||
254 | /** | ||
255 | * Tell a client that we are ready to receive the message. | ||
256 | * | ||
257 | * @param car request that is now ready; the responsibility | ||
258 | * for the handle remains shared between CLIENTS | ||
259 | * and SESSIONS after this call. | ||
260 | */ | ||
261 | void | ||
262 | GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car) | ||
263 | { | ||
264 | struct GSC_Client *c; | ||
265 | struct GNUNET_MQ_Envelope *env; | ||
266 | struct SendMessageReady *smr; | ||
267 | struct GNUNET_TIME_Relative delay; | ||
268 | struct GNUNET_TIME_Relative left; | ||
269 | |||
270 | c = car->client_handle; | ||
271 | if (GNUNET_YES != | ||
272 | GNUNET_CONTAINER_multipeermap_contains (c->connectmap, &car->target)) | ||
273 | { | ||
274 | /* connection has gone down since, drop request */ | ||
275 | GNUNET_assert (0 != | ||
276 | GNUNET_memcmp (&car->target, | ||
277 | &GSC_my_identity)); | ||
278 | GSC_SESSIONS_dequeue_request (car); | ||
279 | GSC_CLIENTS_reject_request (car, GNUNET_NO); | ||
280 | return; | ||
281 | } | ||
282 | delay = GNUNET_TIME_absolute_get_duration (car->received_time); | ||
283 | left = GNUNET_TIME_absolute_get_duration (car->deadline); | ||
284 | if (delay.rel_value_us > GNUNET_CONSTANTS_LATENCY_WARN.rel_value_us) | ||
285 | GNUNET_log ( | ||
286 | GNUNET_ERROR_TYPE_WARNING, | ||
287 | "Client waited %s for permission to transmit to `%s'%s (priority %u)\n", | ||
288 | GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES), | ||
289 | GNUNET_i2s (&car->target), | ||
290 | (0 == left.rel_value_us) ? " (past deadline)" : "", | ||
291 | car->priority); | ||
292 | env = GNUNET_MQ_msg (smr, GNUNET_MESSAGE_TYPE_CORE_SEND_READY); | ||
293 | smr->size = htons (car->msize); | ||
294 | smr->smr_id = car->smr_id; | ||
295 | smr->peer = car->target; | ||
296 | GNUNET_MQ_send (c->mq, env); | ||
297 | } | ||
298 | |||
299 | |||
300 | /** | ||
301 | * Handle #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST message. | ||
302 | * | ||
303 | * @param cls client that sent a #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST | ||
304 | * @param req the `struct SendMessageRequest` | ||
305 | */ | ||
306 | static void | ||
307 | handle_client_send_request (void *cls, const struct SendMessageRequest *req) | ||
308 | { | ||
309 | struct GSC_Client *c = cls; | ||
310 | struct GSC_ClientActiveRequest *car; | ||
311 | int is_loopback; | ||
312 | |||
313 | if (NULL == c->requests) | ||
314 | c->requests = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_NO); | ||
315 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
316 | "Client asked for transmission to `%s'\n", | ||
317 | GNUNET_i2s (&req->peer)); | ||
318 | is_loopback = (0 == GNUNET_memcmp (&req->peer, | ||
319 | &GSC_my_identity)); | ||
320 | if ((! is_loopback) && | ||
321 | (GNUNET_YES != | ||
322 | GNUNET_CONTAINER_multipeermap_contains (c->connectmap, &req->peer))) | ||
323 | { | ||
324 | /* neighbour must have disconnected since request was issued, | ||
325 | * ignore (client will realize it once it processes the | ||
326 | * disconnect notification) */ | ||
327 | GNUNET_STATISTICS_update (GSC_stats, | ||
328 | gettext_noop ( | ||
329 | "# send requests dropped (disconnected)"), | ||
330 | 1, | ||
331 | GNUNET_NO); | ||
332 | GNUNET_SERVICE_client_continue (c->client); | ||
333 | return; | ||
334 | } | ||
335 | |||
336 | car = GNUNET_CONTAINER_multipeermap_get (c->requests, &req->peer); | ||
337 | if (NULL == car) | ||
338 | { | ||
339 | /* create new entry */ | ||
340 | car = GNUNET_new (struct GSC_ClientActiveRequest); | ||
341 | GNUNET_assert (GNUNET_OK == | ||
342 | GNUNET_CONTAINER_multipeermap_put ( | ||
343 | c->requests, | ||
344 | &req->peer, | ||
345 | car, | ||
346 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); | ||
347 | car->client_handle = c; | ||
348 | } | ||
349 | else | ||
350 | { | ||
351 | /* dequeue and recycle memory from pending request, there can only | ||
352 | be at most one per client and peer */ | ||
353 | GNUNET_STATISTICS_update (GSC_stats, | ||
354 | gettext_noop ( | ||
355 | "# dequeuing CAR (duplicate request)"), | ||
356 | 1, | ||
357 | GNUNET_NO); | ||
358 | GSC_SESSIONS_dequeue_request (car); | ||
359 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
360 | "Transmission request to `%s' was a duplicate!\n", | ||
361 | GNUNET_i2s (&req->peer)); | ||
362 | } | ||
363 | car->target = req->peer; | ||
364 | car->received_time = GNUNET_TIME_absolute_get (); | ||
365 | car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline); | ||
366 | car->priority = (enum GNUNET_MQ_PriorityPreferences) ntohl (req->priority); | ||
367 | car->msize = ntohs (req->size); | ||
368 | car->smr_id = req->smr_id; | ||
369 | car->was_solicited = GNUNET_NO; | ||
370 | GNUNET_SERVICE_client_continue (c->client); | ||
371 | if (is_loopback) | ||
372 | { | ||
373 | /* loopback, satisfy immediately */ | ||
374 | GSC_CLIENTS_solicit_request (car); | ||
375 | return; | ||
376 | } | ||
377 | GSC_SESSIONS_queue_request (car); | ||
378 | } | ||
379 | |||
380 | |||
381 | /** | ||
382 | * Closure for the #client_tokenizer_callback(). | ||
383 | */ | ||
384 | struct TokenizerContext | ||
385 | { | ||
386 | /** | ||
387 | * Active request handle for the message. | ||
388 | */ | ||
389 | struct GSC_ClientActiveRequest *car; | ||
390 | |||
391 | /** | ||
392 | * How important is this message. | ||
393 | */ | ||
394 | enum GNUNET_MQ_PriorityPreferences priority; | ||
395 | }; | ||
396 | |||
397 | |||
398 | /** | ||
399 | * Functions with this signature are called whenever a complete | ||
400 | * message is received by the tokenizer. Used by | ||
401 | * #handle_client_send() for dispatching messages from clients to | ||
402 | * either the SESSION subsystem or other CLIENT (for loopback). | ||
403 | * | ||
404 | * @param cls reservation request (`struct TokenizerContext`) | ||
405 | * @param message the actual message | ||
406 | * @return #GNUNET_OK on success, | ||
407 | * #GNUNET_NO to stop further processing (no error) | ||
408 | * #GNUNET_SYSERR to stop further processing with error | ||
409 | */ | ||
410 | static int | ||
411 | tokenized_cb (void *cls, const struct GNUNET_MessageHeader *message) | ||
412 | { | ||
413 | struct TokenizerContext *tc = cls; | ||
414 | struct GSC_ClientActiveRequest *car = tc->car; | ||
415 | char buf[92]; | ||
416 | |||
417 | GNUNET_snprintf (buf, | ||
418 | sizeof(buf), | ||
419 | gettext_noop ("# bytes of messages of type %u received"), | ||
420 | (unsigned int) ntohs (message->type)); | ||
421 | GNUNET_STATISTICS_update (GSC_stats, buf, ntohs (message->size), GNUNET_NO); | ||
422 | if (0 == GNUNET_memcmp (&car->target, | ||
423 | &GSC_my_identity)) | ||
424 | { | ||
425 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
426 | "Delivering message of type %u to myself\n", | ||
427 | ntohs (message->type)); | ||
428 | GSC_CLIENTS_deliver_message (&GSC_my_identity, | ||
429 | message, | ||
430 | ntohs (message->size), | ||
431 | GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND); | ||
432 | GSC_CLIENTS_deliver_message (&GSC_my_identity, | ||
433 | message, | ||
434 | sizeof(struct GNUNET_MessageHeader), | ||
435 | GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND); | ||
436 | GSC_CLIENTS_deliver_message (&GSC_my_identity, | ||
437 | message, | ||
438 | ntohs (message->size), | ||
439 | GNUNET_CORE_OPTION_SEND_FULL_INBOUND); | ||
440 | GSC_CLIENTS_deliver_message (&GSC_my_identity, | ||
441 | message, | ||
442 | sizeof(struct GNUNET_MessageHeader), | ||
443 | GNUNET_CORE_OPTION_SEND_HDR_INBOUND); | ||
444 | } | ||
445 | else | ||
446 | { | ||
447 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
448 | "Delivering message of type %u and size %u to %s\n", | ||
449 | ntohs (message->type), | ||
450 | ntohs (message->size), | ||
451 | GNUNET_i2s (&car->target)); | ||
452 | GSC_CLIENTS_deliver_message (&car->target, | ||
453 | message, | ||
454 | ntohs (message->size), | ||
455 | GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND); | ||
456 | GSC_CLIENTS_deliver_message (&car->target, | ||
457 | message, | ||
458 | sizeof(struct GNUNET_MessageHeader), | ||
459 | GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND); | ||
460 | GSC_SESSIONS_transmit (car, message, tc->priority); | ||
461 | } | ||
462 | return GNUNET_OK; | ||
463 | } | ||
464 | |||
465 | |||
466 | /** | ||
467 | * Check #GNUNET_MESSAGE_TYPE_CORE_SEND request. | ||
468 | * | ||
469 | * @param cls the `struct GSC_Client` | ||
470 | * @param sm the `struct SendMessage` | ||
471 | * @return #GNUNET_OK if @a sm is well-formed | ||
472 | */ | ||
473 | static int | ||
474 | check_client_send (void *cls, const struct SendMessage *sm) | ||
475 | { | ||
476 | return GNUNET_OK; | ||
477 | } | ||
478 | |||
479 | |||
480 | /** | ||
481 | * Handle #GNUNET_MESSAGE_TYPE_CORE_SEND request. | ||
482 | * | ||
483 | * @param cls the `struct GSC_Client` | ||
484 | * @param sm the `struct SendMessage` | ||
485 | */ | ||
486 | static void | ||
487 | handle_client_send (void *cls, const struct SendMessage *sm) | ||
488 | { | ||
489 | struct GSC_Client *c = cls; | ||
490 | struct TokenizerContext tc; | ||
491 | uint16_t msize; | ||
492 | struct GNUNET_TIME_Relative delay; | ||
493 | struct GNUNET_MessageStreamTokenizer *mst; | ||
494 | |||
495 | msize = ntohs (sm->header.size) - sizeof(struct SendMessage); | ||
496 | tc.car = GNUNET_CONTAINER_multipeermap_get (c->requests, &sm->peer); | ||
497 | if (NULL == tc.car) | ||
498 | { | ||
499 | /* Must have been that we first approved the request, then got disconnected | ||
500 | * (which triggered removal of the 'car') and now the client gives us a message | ||
501 | * just *before* the client learns about the disconnect. Theoretically, we | ||
502 | * might also now be *again* connected. So this can happen (but should be | ||
503 | * rare). If it does happen, the message is discarded. */GNUNET_STATISTICS_update (GSC_stats, | ||
504 | gettext_noop ( | ||
505 | "# messages discarded (session disconnected)"), | ||
506 | 1, | ||
507 | GNUNET_NO); | ||
508 | GNUNET_SERVICE_client_continue (c->client); | ||
509 | return; | ||
510 | } | ||
511 | delay = GNUNET_TIME_absolute_get_duration (tc.car->received_time); | ||
512 | tc.priority = (enum GNUNET_MQ_PriorityPreferences) ntohl (sm->priority); | ||
513 | if (delay.rel_value_us > GNUNET_CONSTANTS_LATENCY_WARN.rel_value_us) | ||
514 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
515 | "Client waited %s for transmission of %u bytes to `%s'\n", | ||
516 | GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES), | ||
517 | msize, | ||
518 | GNUNET_i2s (&sm->peer)); | ||
519 | else | ||
520 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
521 | "Client waited %s for transmission of %u bytes to `%s'\n", | ||
522 | GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES), | ||
523 | msize, | ||
524 | GNUNET_i2s (&sm->peer)); | ||
525 | |||
526 | GNUNET_assert ( | ||
527 | GNUNET_YES == | ||
528 | GNUNET_CONTAINER_multipeermap_remove (c->requests, &sm->peer, tc.car)); | ||
529 | mst = GNUNET_MST_create (&tokenized_cb, &tc); | ||
530 | GNUNET_MST_from_buffer (mst, | ||
531 | (const char *) &sm[1], | ||
532 | msize, | ||
533 | GNUNET_YES, | ||
534 | GNUNET_NO); | ||
535 | GNUNET_MST_destroy (mst); | ||
536 | GSC_SESSIONS_dequeue_request (tc.car); | ||
537 | GNUNET_free (tc.car); | ||
538 | GNUNET_SERVICE_client_continue (c->client); | ||
539 | } | ||
540 | |||
541 | |||
542 | /** | ||
543 | * Free client request records. | ||
544 | * | ||
545 | * @param cls NULL | ||
546 | * @param key identity of peer for which this is an active request | ||
547 | * @param value the `struct GSC_ClientActiveRequest` to free | ||
548 | * @return #GNUNET_YES (continue iteration) | ||
549 | */ | ||
550 | static int | ||
551 | destroy_active_client_request (void *cls, | ||
552 | const struct GNUNET_PeerIdentity *key, | ||
553 | void *value) | ||
554 | { | ||
555 | struct GSC_ClientActiveRequest *car = value; | ||
556 | |||
557 | GNUNET_assert ( | ||
558 | GNUNET_YES == | ||
559 | GNUNET_CONTAINER_multipeermap_remove (car->client_handle->requests, | ||
560 | &car->target, | ||
561 | car)); | ||
562 | GSC_SESSIONS_dequeue_request (car); | ||
563 | GNUNET_free (car); | ||
564 | return GNUNET_YES; | ||
565 | } | ||
566 | |||
567 | |||
568 | /** | ||
569 | * A client connected, set up. | ||
570 | * | ||
571 | * @param cls closure | ||
572 | * @param client identification of the client | ||
573 | * @param mq message queue to talk to @a client | ||
574 | * @return our client handle | ||
575 | */ | ||
576 | static void * | ||
577 | client_connect_cb (void *cls, | ||
578 | struct GNUNET_SERVICE_Client *client, | ||
579 | struct GNUNET_MQ_Handle *mq) | ||
580 | { | ||
581 | struct GSC_Client *c; | ||
582 | |||
583 | c = GNUNET_new (struct GSC_Client); | ||
584 | c->client = client; | ||
585 | c->mq = mq; | ||
586 | c->connectmap = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_NO); | ||
587 | GNUNET_CONTAINER_DLL_insert (client_head, client_tail, c); | ||
588 | return c; | ||
589 | } | ||
590 | |||
591 | |||
592 | /** | ||
593 | * A client disconnected, clean up. | ||
594 | * | ||
595 | * @param cls closure | ||
596 | * @param client identification of the client | ||
597 | * @param app_ctx our `struct GST_Client` for @a client | ||
598 | */ | ||
599 | static void | ||
600 | client_disconnect_cb (void *cls, | ||
601 | struct GNUNET_SERVICE_Client *client, | ||
602 | void *app_ctx) | ||
603 | { | ||
604 | struct GSC_Client *c = app_ctx; | ||
605 | |||
606 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
607 | "Client %p has disconnected from core service.\n", | ||
608 | client); | ||
609 | GNUNET_CONTAINER_DLL_remove (client_head, client_tail, c); | ||
610 | if (NULL != c->requests) | ||
611 | { | ||
612 | GNUNET_CONTAINER_multipeermap_iterate (c->requests, | ||
613 | &destroy_active_client_request, | ||
614 | NULL); | ||
615 | GNUNET_CONTAINER_multipeermap_destroy (c->requests); | ||
616 | } | ||
617 | GNUNET_CONTAINER_multipeermap_destroy (c->connectmap); | ||
618 | c->connectmap = NULL; | ||
619 | if (NULL != c->types) | ||
620 | { | ||
621 | GSC_TYPEMAP_remove (c->types, c->tcnt); | ||
622 | GNUNET_free (c->types); | ||
623 | } | ||
624 | GNUNET_free (c); | ||
625 | |||
626 | /* recalculate 'all_client_options' */ | ||
627 | all_client_options = 0; | ||
628 | for (c = client_head; NULL != c; c = c->next) | ||
629 | all_client_options |= c->options; | ||
630 | } | ||
631 | |||
632 | |||
633 | /** | ||
634 | * Notify a particular client about a change to existing connection to | ||
635 | * one of our neighbours (check if the client is interested). Called | ||
636 | * from #GSC_SESSIONS_notify_client_about_sessions(). | ||
637 | * | ||
638 | * @param client client to notify | ||
639 | * @param neighbour identity of the neighbour that changed status | ||
640 | * @param tmap_old previous type map for the neighbour, NULL for connect | ||
641 | * @param tmap_new updated type map for the neighbour, NULL for disconnect | ||
642 | */ | ||
643 | void | ||
644 | GSC_CLIENTS_notify_client_about_neighbour ( | ||
645 | struct GSC_Client *client, | ||
646 | const struct GNUNET_PeerIdentity *neighbour, | ||
647 | const struct GSC_TypeMap *tmap_old, | ||
648 | const struct GSC_TypeMap *tmap_new) | ||
649 | { | ||
650 | struct GNUNET_MQ_Envelope *env; | ||
651 | int old_match; | ||
652 | int new_match; | ||
653 | |||
654 | if (GNUNET_YES != client->got_init) | ||
655 | return; | ||
656 | old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt); | ||
657 | new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt); | ||
658 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
659 | "Notifying client about neighbour %s (%d/%d)\n", | ||
660 | GNUNET_i2s (neighbour), | ||
661 | old_match, | ||
662 | new_match); | ||
663 | if (old_match == new_match) | ||
664 | { | ||
665 | GNUNET_assert ( | ||
666 | old_match == | ||
667 | GNUNET_CONTAINER_multipeermap_contains (client->connectmap, neighbour)); | ||
668 | return; /* no change */ | ||
669 | } | ||
670 | if (GNUNET_NO == old_match) | ||
671 | { | ||
672 | struct ConnectNotifyMessage *cnm; | ||
673 | |||
674 | /* send connect */ | ||
675 | GNUNET_assert ( | ||
676 | GNUNET_NO == | ||
677 | GNUNET_CONTAINER_multipeermap_contains (client->connectmap, neighbour)); | ||
678 | GNUNET_assert (GNUNET_YES == | ||
679 | GNUNET_CONTAINER_multipeermap_put ( | ||
680 | client->connectmap, | ||
681 | neighbour, | ||
682 | NULL, | ||
683 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
684 | env = GNUNET_MQ_msg (cnm, GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); | ||
685 | cnm->reserved = htonl (0); | ||
686 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
687 | "Sending NOTIFY_CONNECT message about peer %s to client.\n", | ||
688 | GNUNET_i2s (neighbour)); | ||
689 | cnm->peer = *neighbour; | ||
690 | GNUNET_MQ_send (client->mq, env); | ||
691 | } | ||
692 | else | ||
693 | { | ||
694 | struct DisconnectNotifyMessage *dcm; | ||
695 | |||
696 | /* send disconnect */ | ||
697 | GNUNET_assert ( | ||
698 | GNUNET_YES == | ||
699 | GNUNET_CONTAINER_multipeermap_contains (client->connectmap, neighbour)); | ||
700 | GNUNET_assert (GNUNET_YES == | ||
701 | GNUNET_CONTAINER_multipeermap_remove (client->connectmap, | ||
702 | neighbour, | ||
703 | NULL)); | ||
704 | env = GNUNET_MQ_msg (dcm, GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT); | ||
705 | dcm->reserved = htonl (0); | ||
706 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
707 | "Sending NOTIFY_DISCONNECT message about peer %s to client.\n", | ||
708 | GNUNET_i2s (neighbour)); | ||
709 | dcm->peer = *neighbour; | ||
710 | GNUNET_MQ_send (client->mq, env); | ||
711 | } | ||
712 | } | ||
713 | |||
714 | |||
715 | /** | ||
716 | * Notify all clients about a change to existing session. | ||
717 | * Called from SESSIONS whenever there is a change in sessions | ||
718 | * or types processed by the respective peer. | ||
719 | * | ||
720 | * @param neighbour identity of the neighbour that changed status | ||
721 | * @param tmap_old previous type map for the neighbour, NULL for connect | ||
722 | * @param tmap_new updated type map for the neighbour, NULL for disconnect | ||
723 | */ | ||
724 | void | ||
725 | GSC_CLIENTS_notify_clients_about_neighbour ( | ||
726 | const struct GNUNET_PeerIdentity *neighbour, | ||
727 | const struct GSC_TypeMap *tmap_old, | ||
728 | const struct GSC_TypeMap *tmap_new) | ||
729 | { | ||
730 | struct GSC_Client *c; | ||
731 | |||
732 | for (c = client_head; NULL != c; c = c->next) | ||
733 | GSC_CLIENTS_notify_client_about_neighbour (c, | ||
734 | neighbour, | ||
735 | tmap_old, | ||
736 | tmap_new); | ||
737 | } | ||
738 | |||
739 | |||
740 | /** | ||
741 | * Deliver P2P message to interested clients. Caller must have checked | ||
742 | * that the sending peer actually lists the given message type as one | ||
743 | * of its types. | ||
744 | * | ||
745 | * @param sender peer who sent us the message | ||
746 | * @param msg the message | ||
747 | * @param msize number of bytes to transmit | ||
748 | * @param options options for checking which clients should | ||
749 | * receive the message | ||
750 | */ | ||
751 | void | ||
752 | GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender, | ||
753 | const struct GNUNET_MessageHeader *msg, | ||
754 | uint16_t msize, | ||
755 | uint32_t options) | ||
756 | { | ||
757 | size_t size = msize + sizeof(struct NotifyTrafficMessage); | ||
758 | |||
759 | if (size >= GNUNET_MAX_MESSAGE_SIZE) | ||
760 | { | ||
761 | GNUNET_break (0); | ||
762 | return; | ||
763 | } | ||
764 | if (! ((0 != (all_client_options & options)) || | ||
765 | (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)))) | ||
766 | return; /* no client cares about this message notification */ | ||
767 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
768 | "Core service passes message from `%s' of type %u to client.\n", | ||
769 | GNUNET_i2s (sender), | ||
770 | (unsigned int) ntohs (msg->type)); | ||
771 | GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type)); | ||
772 | |||
773 | for (struct GSC_Client *c = client_head; NULL != c; c = c->next) | ||
774 | { | ||
775 | struct GNUNET_MQ_Envelope *env; | ||
776 | struct NotifyTrafficMessage *ntm; | ||
777 | uint16_t mtype; | ||
778 | unsigned int qlen; | ||
779 | int tm; | ||
780 | |||
781 | tm = type_match (ntohs (msg->type), c); | ||
782 | if (! ((0 != (c->options & options)) || | ||
783 | ((0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) && | ||
784 | (GNUNET_YES == tm)))) | ||
785 | continue; /* neither options nor type match permit the message */ | ||
786 | if ((0 != (options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND)) && | ||
787 | ((0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) || | ||
788 | (GNUNET_YES == tm))) | ||
789 | continue; | ||
790 | if ((0 != (options & GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND)) && | ||
791 | (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND))) | ||
792 | continue; | ||
793 | |||
794 | /* Drop messages if: | ||
795 | 1) We are above the hard limit, or | ||
796 | 2) We are above the soft limit, and a coin toss limited | ||
797 | to the message size (giving larger messages a | ||
798 | proportionally higher chance of being queued) falls | ||
799 | below the threshold. The threshold is based on where | ||
800 | we are between the soft and the hard limit, scaled | ||
801 | to match the range of message sizes we usually encounter | ||
802 | (i.e. up to 32k); so a 64k message has a 50% chance of | ||
803 | being kept if we are just barely below the hard max, | ||
804 | and a 99% chance of being kept if we are at the soft max. | ||
805 | The reason is to make it more likely to drop control traffic | ||
806 | (ACK, queries) which may be cumulative or highly redundant, | ||
807 | and cheap to drop than data traffic. */qlen = GNUNET_MQ_get_length (c->mq); | ||
808 | if ((qlen >= HARD_MAX_QUEUE) || | ||
809 | ((qlen > SOFT_MAX_QUEUE) && | ||
810 | ((GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
811 | ntohs (msg->size))) < | ||
812 | (qlen - SOFT_MAX_QUEUE) * 0x8000 | ||
813 | / (HARD_MAX_QUEUE - SOFT_MAX_QUEUE)))) | ||
814 | { | ||
815 | char buf[1024]; | ||
816 | |||
817 | GNUNET_log ( | ||
818 | GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK, | ||
819 | "Dropping decrypted message of type %u as client is too busy (queue full)\n", | ||
820 | (unsigned int) ntohs (msg->type)); | ||
821 | GNUNET_snprintf (buf, | ||
822 | sizeof(buf), | ||
823 | gettext_noop ( | ||
824 | "# messages of type %u discarded (client busy)"), | ||
825 | (unsigned int) ntohs (msg->type)); | ||
826 | GNUNET_STATISTICS_update (GSC_stats, buf, 1, GNUNET_NO); | ||
827 | continue; | ||
828 | } | ||
829 | |||
830 | GNUNET_log ( | ||
831 | GNUNET_ERROR_TYPE_DEBUG, | ||
832 | "Sending %u message with %u bytes to client interested in messages of type %u.\n", | ||
833 | options, | ||
834 | ntohs (msg->size), | ||
835 | (unsigned int) ntohs (msg->type)); | ||
836 | |||
837 | if (0 != (options & (GNUNET_CORE_OPTION_SEND_FULL_INBOUND | ||
838 | | GNUNET_CORE_OPTION_SEND_HDR_INBOUND))) | ||
839 | mtype = GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND; | ||
840 | else | ||
841 | mtype = GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND; | ||
842 | env = GNUNET_MQ_msg_extra (ntm, msize, mtype); | ||
843 | ntm->peer = *sender; | ||
844 | GNUNET_memcpy (&ntm[1], msg, msize); | ||
845 | |||
846 | GNUNET_assert ( | ||
847 | (0 == (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) || | ||
848 | (GNUNET_YES != tm) || | ||
849 | (GNUNET_YES == | ||
850 | GNUNET_CONTAINER_multipeermap_contains (c->connectmap, sender))); | ||
851 | GNUNET_MQ_send (c->mq, env); | ||
852 | } | ||
853 | } | ||
854 | |||
855 | |||
856 | /** | ||
857 | * Last task run during shutdown. Disconnects us from | ||
858 | * the transport. | ||
859 | * | ||
860 | * @param cls NULL, unused | ||
861 | */ | ||
862 | static void | ||
863 | shutdown_task (void *cls) | ||
864 | { | ||
865 | struct GSC_Client *c; | ||
866 | |||
867 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service shutting down.\n"); | ||
868 | while (NULL != (c = client_head)) | ||
869 | GNUNET_SERVICE_client_drop (c->client); | ||
870 | GSC_SESSIONS_done (); | ||
871 | GSC_KX_done (); | ||
872 | GSC_TYPEMAP_done (); | ||
873 | if (NULL != GSC_stats) | ||
874 | { | ||
875 | GNUNET_STATISTICS_destroy (GSC_stats, GNUNET_NO); | ||
876 | GSC_stats = NULL; | ||
877 | } | ||
878 | GSC_cfg = NULL; | ||
879 | } | ||
880 | |||
881 | |||
882 | /** | ||
883 | * Handle #GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request. For this | ||
884 | * request type, the client does not have to have transmitted an INIT | ||
885 | * request. All current peers are returned, regardless of which | ||
886 | * message types they accept. | ||
887 | * | ||
888 | * @param cls client sending the iteration request | ||
889 | * @param message iteration request message | ||
890 | */ | ||
891 | static void | ||
892 | handle_client_monitor_peers (void *cls, | ||
893 | const struct GNUNET_MessageHeader *message) | ||
894 | { | ||
895 | struct GSC_Client *c = cls; | ||
896 | |||
897 | GNUNET_SERVICE_client_continue (c->client); | ||
898 | GSC_KX_handle_client_monitor_peers (c->mq); | ||
899 | } | ||
900 | |||
901 | |||
902 | /** | ||
903 | * Initiate core service. | ||
904 | * | ||
905 | * @param cls closure | ||
906 | * @param c configuration to use | ||
907 | * @param service the initialized service | ||
908 | */ | ||
909 | static void | ||
910 | run (void *cls, | ||
911 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
912 | struct GNUNET_SERVICE_Handle *service) | ||
913 | { | ||
914 | struct GNUNET_CRYPTO_EddsaPrivateKey pk; | ||
915 | char *keyfile; | ||
916 | |||
917 | GSC_cfg = c; | ||
918 | if (GNUNET_OK != | ||
919 | GNUNET_CONFIGURATION_get_value_filename (GSC_cfg, | ||
920 | "PEER", | ||
921 | "PRIVATE_KEY", | ||
922 | &keyfile)) | ||
923 | { | ||
924 | GNUNET_log ( | ||
925 | GNUNET_ERROR_TYPE_ERROR, | ||
926 | _ ("Core service is lacking HOSTKEY configuration setting. Exiting.\n")); | ||
927 | GNUNET_SCHEDULER_shutdown (); | ||
928 | return; | ||
929 | } | ||
930 | GSC_stats = GNUNET_STATISTICS_create ("core", GSC_cfg); | ||
931 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
932 | GNUNET_SERVICE_suspend (service); | ||
933 | GSC_TYPEMAP_init (); | ||
934 | if (GNUNET_SYSERR == | ||
935 | GNUNET_CRYPTO_eddsa_key_from_file (keyfile, | ||
936 | GNUNET_YES, | ||
937 | &pk)) | ||
938 | { | ||
939 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
940 | "Failed to setup peer's private key\n"); | ||
941 | GNUNET_SCHEDULER_shutdown (); | ||
942 | GNUNET_free (keyfile); | ||
943 | return; | ||
944 | } | ||
945 | GNUNET_free (keyfile); | ||
946 | if (GNUNET_OK != GSC_KX_init (&pk)) | ||
947 | { | ||
948 | GNUNET_SCHEDULER_shutdown (); | ||
949 | return; | ||
950 | } | ||
951 | GSC_SESSIONS_init (); | ||
952 | GNUNET_SERVICE_resume (service); | ||
953 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
954 | _ ("Core service of `%s' ready.\n"), | ||
955 | GNUNET_i2s (&GSC_my_identity)); | ||
956 | } | ||
957 | |||
958 | |||
959 | /** | ||
960 | * Define "main" method using service macro. | ||
961 | */ | ||
962 | GNUNET_SERVICE_MAIN ( | ||
963 | "core", | ||
964 | GNUNET_SERVICE_OPTION_NONE, | ||
965 | &run, | ||
966 | &client_connect_cb, | ||
967 | &client_disconnect_cb, | ||
968 | NULL, | ||
969 | GNUNET_MQ_hd_var_size (client_init, | ||
970 | GNUNET_MESSAGE_TYPE_CORE_INIT, | ||
971 | struct InitMessage, | ||
972 | NULL), | ||
973 | GNUNET_MQ_hd_fixed_size (client_monitor_peers, | ||
974 | GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS, | ||
975 | struct GNUNET_MessageHeader, | ||
976 | NULL), | ||
977 | GNUNET_MQ_hd_fixed_size (client_send_request, | ||
978 | GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST, | ||
979 | struct SendMessageRequest, | ||
980 | NULL), | ||
981 | GNUNET_MQ_hd_var_size (client_send, | ||
982 | GNUNET_MESSAGE_TYPE_CORE_SEND, | ||
983 | struct SendMessage, | ||
984 | NULL), | ||
985 | GNUNET_MQ_handler_end ()); | ||
986 | |||
987 | |||
988 | /* end of gnunet-service-core.c */ | ||
diff --git a/src/service/core/gnunet-service-core.h b/src/service/core/gnunet-service-core.h new file mode 100644 index 000000000..0f71f221a --- /dev/null +++ b/src/service/core/gnunet-service-core.h | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2011 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core.h | ||
23 | * @brief Globals for gnunet-service-core | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef GNUNET_SERVICE_CORE_H | ||
27 | #define GNUNET_SERVICE_CORE_H | ||
28 | |||
29 | #include "gnunet_statistics_service.h" | ||
30 | #include "gnunet_core_service.h" | ||
31 | #include "core.h" | ||
32 | #include "gnunet-service-core_typemap.h" | ||
33 | |||
34 | |||
35 | /** | ||
36 | * Opaque handle to a client. | ||
37 | */ | ||
38 | struct GSC_Client; | ||
39 | |||
40 | |||
41 | /** | ||
42 | * Record kept for each request for transmission issued by a | ||
43 | * client that is still pending. (This struct is used by | ||
44 | * both the 'CLIENTS' and 'SESSIONS' subsystems.) | ||
45 | */ | ||
46 | struct GSC_ClientActiveRequest | ||
47 | { | ||
48 | /** | ||
49 | * Active requests are kept in a doubly-linked list of | ||
50 | * the respective target peer. | ||
51 | */ | ||
52 | struct GSC_ClientActiveRequest *next; | ||
53 | |||
54 | /** | ||
55 | * Active requests are kept in a doubly-linked list of | ||
56 | * the respective target peer. | ||
57 | */ | ||
58 | struct GSC_ClientActiveRequest *prev; | ||
59 | |||
60 | /** | ||
61 | * Handle to the client. | ||
62 | */ | ||
63 | struct GSC_Client *client_handle; | ||
64 | |||
65 | /** | ||
66 | * Which peer is the message going to be for? | ||
67 | */ | ||
68 | struct GNUNET_PeerIdentity target; | ||
69 | |||
70 | /** | ||
71 | * At what time did we first see this request? | ||
72 | */ | ||
73 | struct GNUNET_TIME_Absolute received_time; | ||
74 | |||
75 | /** | ||
76 | * By what time would the client want to see this message out? | ||
77 | */ | ||
78 | struct GNUNET_TIME_Absolute deadline; | ||
79 | |||
80 | /** | ||
81 | * How important is this request. | ||
82 | */ | ||
83 | enum GNUNET_MQ_PriorityPreferences priority; | ||
84 | |||
85 | /** | ||
86 | * Has this request been solicited yet? | ||
87 | */ | ||
88 | int was_solicited; | ||
89 | |||
90 | /** | ||
91 | * How many bytes does the client intend to send? | ||
92 | */ | ||
93 | uint16_t msize; | ||
94 | |||
95 | /** | ||
96 | * Unique request ID (in big endian). | ||
97 | */ | ||
98 | uint16_t smr_id; | ||
99 | }; | ||
100 | |||
101 | |||
102 | /** | ||
103 | * Tell a client that we are ready to receive the message. | ||
104 | * | ||
105 | * @param car request that is now ready; the responsibility | ||
106 | * for the handle remains shared between CLIENTS | ||
107 | * and SESSIONS after this call. | ||
108 | */ | ||
109 | void | ||
110 | GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car); | ||
111 | |||
112 | |||
113 | /** | ||
114 | * We will never be ready to transmit the given message in (disconnect | ||
115 | * or invalid request). Frees resources associated with @a car. We | ||
116 | * don't explicitly tell the client, it'll learn with the disconnect | ||
117 | * (or violated the protocol). | ||
118 | * | ||
119 | * @param car request that now permanently failed; the | ||
120 | * responsibility for the handle is now returned | ||
121 | * to CLIENTS (SESSIONS is done with it). | ||
122 | * @param drop_client #GNUNET_YES if the client violated the protocol | ||
123 | * and we should thus drop the connection | ||
124 | */ | ||
125 | void | ||
126 | GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car, | ||
127 | int drop_client); | ||
128 | |||
129 | |||
130 | /** | ||
131 | * Notify a particular client about a change to existing connection to | ||
132 | * one of our neighbours (check if the client is interested). Called | ||
133 | * from #GSC_SESSIONS_notify_client_about_sessions(). | ||
134 | * | ||
135 | * @param client client to notify | ||
136 | * @param neighbour identity of the neighbour that changed status | ||
137 | * @param tmap_old previous type map for the neighbour, NULL for connect | ||
138 | * @param tmap_new updated type map for the neighbour, NULL for disconnect | ||
139 | */ | ||
140 | void | ||
141 | GSC_CLIENTS_notify_client_about_neighbour ( | ||
142 | struct GSC_Client *client, | ||
143 | const struct GNUNET_PeerIdentity *neighbour, | ||
144 | const struct GSC_TypeMap *tmap_old, | ||
145 | const struct GSC_TypeMap *tmap_new); | ||
146 | |||
147 | |||
148 | /** | ||
149 | * Deliver P2P message to interested clients. Caller must have checked | ||
150 | * that the sending peer actually lists the given message type as one | ||
151 | * of its types. | ||
152 | * | ||
153 | * @param sender peer who sent us the message | ||
154 | * @param msg the message | ||
155 | * @param msize number of bytes to transmit | ||
156 | * @param options options for checking which clients should | ||
157 | * receive the message | ||
158 | */ | ||
159 | void | ||
160 | GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender, | ||
161 | const struct GNUNET_MessageHeader *msg, | ||
162 | uint16_t msize, | ||
163 | uint32_t options); | ||
164 | |||
165 | |||
166 | /** | ||
167 | * Notify all clients about a change to existing session. | ||
168 | * Called from SESSIONS whenever there is a change in sessions | ||
169 | * or types processed by the respective peer. | ||
170 | * | ||
171 | * @param neighbour identity of the neighbour that changed status | ||
172 | * @param tmap_old previous type map for the neighbour, NULL for connect | ||
173 | * @param tmap_new updated type map for the neighbour, NULL for disconnect | ||
174 | */ | ||
175 | void | ||
176 | GSC_CLIENTS_notify_clients_about_neighbour ( | ||
177 | const struct GNUNET_PeerIdentity *neighbour, | ||
178 | const struct GSC_TypeMap *tmap_old, | ||
179 | const struct GSC_TypeMap *tmap_new); | ||
180 | |||
181 | |||
182 | /** | ||
183 | * Our configuration. | ||
184 | */ | ||
185 | extern const struct GNUNET_CONFIGURATION_Handle *GSC_cfg; | ||
186 | |||
187 | /** | ||
188 | * For creating statistics. | ||
189 | */ | ||
190 | extern struct GNUNET_STATISTICS_Handle *GSC_stats; | ||
191 | |||
192 | /** | ||
193 | * Our identity. | ||
194 | */ | ||
195 | extern struct GNUNET_PeerIdentity GSC_my_identity; | ||
196 | |||
197 | |||
198 | #endif | ||
diff --git a/src/service/core/gnunet-service-core_kx.c b/src/service/core/gnunet-service-core_kx.c new file mode 100644 index 000000000..c5a1de769 --- /dev/null +++ b/src/service/core/gnunet-service-core_kx.c | |||
@@ -0,0 +1,1945 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009-2013, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core_kx.c | ||
23 | * @brief code for managing the key exchange (SET_KEY, PING, PONG) with other | ||
24 | * peers | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet-service-core_kx.h" | ||
29 | #include "gnunet_transport_core_service.h" | ||
30 | #include "gnunet-service-core_sessions.h" | ||
31 | #include "gnunet-service-core.h" | ||
32 | #include "gnunet_constants.h" | ||
33 | #include "gnunet_signatures.h" | ||
34 | #include "gnunet_protocols.h" | ||
35 | |||
36 | /** | ||
37 | * Enable expensive (and possibly problematic for privacy!) logging of KX. | ||
38 | */ | ||
39 | #define DEBUG_KX 0 | ||
40 | |||
41 | /** | ||
42 | * How long do we wait for SET_KEY confirmation initially? | ||
43 | */ | ||
44 | #define INITIAL_SET_KEY_RETRY_FREQUENCY \ | ||
45 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) | ||
46 | |||
47 | /** | ||
48 | * What is the minimum frequency for a PING message? | ||
49 | */ | ||
50 | #define MIN_PING_FREQUENCY \ | ||
51 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
52 | |||
53 | /** | ||
54 | * How often do we rekey? | ||
55 | */ | ||
56 | #define REKEY_FREQUENCY \ | ||
57 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12) | ||
58 | |||
59 | /** | ||
60 | * What time difference do we tolerate? | ||
61 | */ | ||
62 | #define REKEY_TOLERANCE \ | ||
63 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) | ||
64 | |||
65 | /** | ||
66 | * What is the maximum age of a message for us to consider processing | ||
67 | * it? Note that this looks at the timestamp used by the other peer, | ||
68 | * so clock skew between machines does come into play here. So this | ||
69 | * should be picked high enough so that a little bit of clock skew | ||
70 | * does not prevent peers from connecting to us. | ||
71 | */ | ||
72 | #define MAX_MESSAGE_AGE GNUNET_TIME_UNIT_DAYS | ||
73 | |||
74 | |||
75 | GNUNET_NETWORK_STRUCT_BEGIN | ||
76 | |||
77 | /** | ||
78 | * Encapsulation for encrypted messages exchanged between | ||
79 | * peers. Followed by the actual encrypted data. | ||
80 | */ | ||
81 | struct EncryptedMessage | ||
82 | { | ||
83 | /** | ||
84 | * Message type is #GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE. | ||
85 | */ | ||
86 | struct GNUNET_MessageHeader header; | ||
87 | |||
88 | /** | ||
89 | * Random value used for IV generation. | ||
90 | */ | ||
91 | uint32_t iv_seed GNUNET_PACKED; | ||
92 | |||
93 | /** | ||
94 | * MAC of the encrypted message (starting at @e sequence_number), | ||
95 | * used to verify message integrity. Everything after this value | ||
96 | * (excluding this value itself) will be encrypted and | ||
97 | * authenticated. #ENCRYPTED_HEADER_SIZE must be set to the offset | ||
98 | * of the *next* field. | ||
99 | */ | ||
100 | struct GNUNET_HashCode hmac; | ||
101 | |||
102 | /** | ||
103 | * Sequence number, in network byte order. This field | ||
104 | * must be the first encrypted/decrypted field | ||
105 | */ | ||
106 | uint32_t sequence_number GNUNET_PACKED; | ||
107 | |||
108 | /** | ||
109 | * Reserved, always zero. | ||
110 | */ | ||
111 | uint32_t reserved GNUNET_PACKED; | ||
112 | |||
113 | /** | ||
114 | * Timestamp. Used to prevent replay of ancient messages | ||
115 | * (recent messages are caught with the sequence number). | ||
116 | */ | ||
117 | struct GNUNET_TIME_AbsoluteNBO timestamp; | ||
118 | }; | ||
119 | GNUNET_NETWORK_STRUCT_END | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Number of bytes (at the beginning) of `struct EncryptedMessage` | ||
124 | * that are NOT encrypted. | ||
125 | */ | ||
126 | #define ENCRYPTED_HEADER_SIZE \ | ||
127 | (offsetof (struct EncryptedMessage, sequence_number)) | ||
128 | |||
129 | |||
130 | /** | ||
131 | * Information about the status of a key exchange with another peer. | ||
132 | */ | ||
133 | struct GSC_KeyExchangeInfo | ||
134 | { | ||
135 | /** | ||
136 | * DLL. | ||
137 | */ | ||
138 | struct GSC_KeyExchangeInfo *next; | ||
139 | |||
140 | /** | ||
141 | * DLL. | ||
142 | */ | ||
143 | struct GSC_KeyExchangeInfo *prev; | ||
144 | |||
145 | /** | ||
146 | * Identity of the peer. | ||
147 | */ | ||
148 | const struct GNUNET_PeerIdentity *peer; | ||
149 | |||
150 | /** | ||
151 | * Message queue for sending messages to @a peer. | ||
152 | */ | ||
153 | struct GNUNET_MQ_Handle *mq; | ||
154 | |||
155 | /** | ||
156 | * Our message stream tokenizer (for encrypted payload). | ||
157 | */ | ||
158 | struct GNUNET_MessageStreamTokenizer *mst; | ||
159 | |||
160 | /** | ||
161 | * PING message we transmit to the other peer. | ||
162 | */ | ||
163 | struct PingMessage ping; | ||
164 | |||
165 | /** | ||
166 | * Ephemeral public ECC key of the other peer. | ||
167 | */ | ||
168 | struct GNUNET_CRYPTO_EcdhePublicKey other_ephemeral_key; | ||
169 | |||
170 | /** | ||
171 | * Key we use to encrypt our messages for the other peer | ||
172 | * (initialized by us when we do the handshake). | ||
173 | */ | ||
174 | struct GNUNET_CRYPTO_SymmetricSessionKey encrypt_key; | ||
175 | |||
176 | /** | ||
177 | * Key we use to decrypt messages from the other peer | ||
178 | * (given to us by the other peer during the handshake). | ||
179 | */ | ||
180 | struct GNUNET_CRYPTO_SymmetricSessionKey decrypt_key; | ||
181 | |||
182 | /** | ||
183 | * At what time did the other peer generate the decryption key? | ||
184 | */ | ||
185 | struct GNUNET_TIME_Absolute foreign_key_expires; | ||
186 | |||
187 | /** | ||
188 | * When should the session time out (if there are no PONGs)? | ||
189 | */ | ||
190 | struct GNUNET_TIME_Absolute timeout; | ||
191 | |||
192 | /** | ||
193 | * What was the last timeout we informed our monitors about? | ||
194 | */ | ||
195 | struct GNUNET_TIME_Absolute last_notify_timeout; | ||
196 | |||
197 | /** | ||
198 | * At what frequency are we currently re-trying SET_KEY messages? | ||
199 | */ | ||
200 | struct GNUNET_TIME_Relative set_key_retry_frequency; | ||
201 | |||
202 | /** | ||
203 | * ID of task used for re-trying SET_KEY and PING message. | ||
204 | */ | ||
205 | struct GNUNET_SCHEDULER_Task *retry_set_key_task; | ||
206 | |||
207 | /** | ||
208 | * ID of task used for sending keep-alive pings. | ||
209 | */ | ||
210 | struct GNUNET_SCHEDULER_Task *keep_alive_task; | ||
211 | |||
212 | /** | ||
213 | * Bit map indicating which of the 32 sequence numbers before the | ||
214 | * last were received (good for accepting out-of-order packets and | ||
215 | * estimating reliability of the connection) | ||
216 | */ | ||
217 | uint32_t last_packets_bitmap; | ||
218 | |||
219 | /** | ||
220 | * last sequence number received on this connection (highest) | ||
221 | */ | ||
222 | uint32_t last_sequence_number_received; | ||
223 | |||
224 | /** | ||
225 | * last sequence number transmitted | ||
226 | */ | ||
227 | uint32_t last_sequence_number_sent; | ||
228 | |||
229 | /** | ||
230 | * What was our PING challenge number (for this peer)? | ||
231 | */ | ||
232 | uint32_t ping_challenge; | ||
233 | |||
234 | /** | ||
235 | * #GNUNET_YES if this peer currently has excess bandwidth. | ||
236 | */ | ||
237 | int has_excess_bandwidth; | ||
238 | |||
239 | /** | ||
240 | * What is our connection status? | ||
241 | */ | ||
242 | enum GNUNET_CORE_KxState status; | ||
243 | }; | ||
244 | |||
245 | |||
246 | /** | ||
247 | * Transport service. | ||
248 | */ | ||
249 | static struct GNUNET_TRANSPORT_CoreHandle *transport; | ||
250 | |||
251 | /** | ||
252 | * Our private key. | ||
253 | */ | ||
254 | static struct GNUNET_CRYPTO_EddsaPrivateKey my_private_key; | ||
255 | |||
256 | /** | ||
257 | * Our ephemeral private key. | ||
258 | */ | ||
259 | static struct GNUNET_CRYPTO_EcdhePrivateKey my_ephemeral_key; | ||
260 | |||
261 | /** | ||
262 | * Current message we send for a key exchange. | ||
263 | */ | ||
264 | static struct EphemeralKeyMessage current_ekm; | ||
265 | |||
266 | /** | ||
267 | * DLL head. | ||
268 | */ | ||
269 | static struct GSC_KeyExchangeInfo *kx_head; | ||
270 | |||
271 | /** | ||
272 | * DLL tail. | ||
273 | */ | ||
274 | static struct GSC_KeyExchangeInfo *kx_tail; | ||
275 | |||
276 | /** | ||
277 | * Task scheduled for periodic re-generation (and thus rekeying) of our | ||
278 | * ephemeral key. | ||
279 | */ | ||
280 | static struct GNUNET_SCHEDULER_Task *rekey_task; | ||
281 | |||
282 | /** | ||
283 | * Notification context for broadcasting to monitors. | ||
284 | */ | ||
285 | static struct GNUNET_NotificationContext *nc; | ||
286 | |||
287 | |||
288 | /** | ||
289 | * Calculate seed value we should use for a message. | ||
290 | * | ||
291 | * @param kx key exchange context | ||
292 | */ | ||
293 | static uint32_t | ||
294 | calculate_seed (struct GSC_KeyExchangeInfo *kx) | ||
295 | { | ||
296 | /* Note: may want to make this non-random and instead | ||
297 | derive from key material to avoid having an undetectable | ||
298 | side-channel */ | ||
299 | return htonl ( | ||
300 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX)); | ||
301 | } | ||
302 | |||
303 | |||
304 | /** | ||
305 | * Inform all monitors about the KX state of the given peer. | ||
306 | * | ||
307 | * @param kx key exchange state to inform about | ||
308 | */ | ||
309 | static void | ||
310 | monitor_notify_all (struct GSC_KeyExchangeInfo *kx) | ||
311 | { | ||
312 | struct MonitorNotifyMessage msg; | ||
313 | |||
314 | msg.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY); | ||
315 | msg.header.size = htons (sizeof(msg)); | ||
316 | msg.state = htonl ((uint32_t) kx->status); | ||
317 | msg.peer = *kx->peer; | ||
318 | msg.timeout = GNUNET_TIME_absolute_hton (kx->timeout); | ||
319 | GNUNET_notification_context_broadcast (nc, &msg.header, GNUNET_NO); | ||
320 | kx->last_notify_timeout = kx->timeout; | ||
321 | } | ||
322 | |||
323 | |||
324 | /** | ||
325 | * Derive an authentication key from "set key" information | ||
326 | * | ||
327 | * @param akey authentication key to derive | ||
328 | * @param skey session key to use | ||
329 | * @param seed seed to use | ||
330 | */ | ||
331 | static void | ||
332 | derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey, | ||
333 | const struct GNUNET_CRYPTO_SymmetricSessionKey *skey, | ||
334 | uint32_t seed) | ||
335 | { | ||
336 | static const char ctx[] = "authentication key"; | ||
337 | |||
338 | #if DEBUG_KX | ||
339 | struct GNUNET_HashCode sh; | ||
340 | |||
341 | GNUNET_CRYPTO_hash (skey, sizeof(*skey), &sh); | ||
342 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
343 | "Deriving Auth key from SKEY %s and seed %u\n", | ||
344 | GNUNET_h2s (&sh), | ||
345 | (unsigned int) seed); | ||
346 | #endif | ||
347 | GNUNET_CRYPTO_hmac_derive_key (akey, | ||
348 | skey, | ||
349 | &seed, | ||
350 | sizeof(seed), | ||
351 | skey, | ||
352 | sizeof( | ||
353 | struct GNUNET_CRYPTO_SymmetricSessionKey), | ||
354 | ctx, | ||
355 | sizeof(ctx), | ||
356 | NULL); | ||
357 | } | ||
358 | |||
359 | |||
360 | /** | ||
361 | * Derive an IV from packet information | ||
362 | * | ||
363 | * @param iv initialization vector to initialize | ||
364 | * @param skey session key to use | ||
365 | * @param seed seed to use | ||
366 | * @param identity identity of the other peer to use | ||
367 | */ | ||
368 | static void | ||
369 | derive_iv (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, | ||
370 | const struct GNUNET_CRYPTO_SymmetricSessionKey *skey, | ||
371 | uint32_t seed, | ||
372 | const struct GNUNET_PeerIdentity *identity) | ||
373 | { | ||
374 | static const char ctx[] = "initialization vector"; | ||
375 | |||
376 | #if DEBUG_KX | ||
377 | struct GNUNET_HashCode sh; | ||
378 | |||
379 | GNUNET_CRYPTO_hash (skey, sizeof(*skey), &sh); | ||
380 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
381 | "Deriving IV from SKEY %s and seed %u for peer %s\n", | ||
382 | GNUNET_h2s (&sh), | ||
383 | (unsigned int) seed, | ||
384 | GNUNET_i2s (identity)); | ||
385 | #endif | ||
386 | GNUNET_CRYPTO_symmetric_derive_iv (iv, | ||
387 | skey, | ||
388 | &seed, | ||
389 | sizeof(seed), | ||
390 | identity, | ||
391 | sizeof(struct GNUNET_PeerIdentity), | ||
392 | ctx, | ||
393 | sizeof(ctx), | ||
394 | NULL); | ||
395 | } | ||
396 | |||
397 | |||
398 | /** | ||
399 | * Derive an IV from pong packet information | ||
400 | * | ||
401 | * @param iv initialization vector to initialize | ||
402 | * @param skey session key to use | ||
403 | * @param seed seed to use | ||
404 | * @param challenge nonce to use | ||
405 | * @param identity identity of the other peer to use | ||
406 | */ | ||
407 | static void | ||
408 | derive_pong_iv (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, | ||
409 | const struct GNUNET_CRYPTO_SymmetricSessionKey *skey, | ||
410 | uint32_t seed, | ||
411 | uint32_t challenge, | ||
412 | const struct GNUNET_PeerIdentity *identity) | ||
413 | { | ||
414 | static const char ctx[] = "pong initialization vector"; | ||
415 | |||
416 | #if DEBUG_KX | ||
417 | struct GNUNET_HashCode sh; | ||
418 | |||
419 | GNUNET_CRYPTO_hash (skey, sizeof(*skey), &sh); | ||
420 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
421 | "Deriving PONG IV from SKEY %s and seed %u/%u for %s\n", | ||
422 | GNUNET_h2s (&sh), | ||
423 | (unsigned int) seed, | ||
424 | (unsigned int) challenge, | ||
425 | GNUNET_i2s (identity)); | ||
426 | #endif | ||
427 | GNUNET_CRYPTO_symmetric_derive_iv (iv, | ||
428 | skey, | ||
429 | &seed, | ||
430 | sizeof(seed), | ||
431 | identity, | ||
432 | sizeof(struct GNUNET_PeerIdentity), | ||
433 | &challenge, | ||
434 | sizeof(challenge), | ||
435 | ctx, | ||
436 | sizeof(ctx), | ||
437 | NULL); | ||
438 | } | ||
439 | |||
440 | |||
441 | /** | ||
442 | * Derive an AES key from key material | ||
443 | * | ||
444 | * @param sender peer identity of the sender | ||
445 | * @param receiver peer identity of the sender | ||
446 | * @param key_material high entropy key material to use | ||
447 | * @param skey set to derived session key | ||
448 | */ | ||
449 | static void | ||
450 | derive_aes_key (const struct GNUNET_PeerIdentity *sender, | ||
451 | const struct GNUNET_PeerIdentity *receiver, | ||
452 | const struct GNUNET_HashCode *key_material, | ||
453 | struct GNUNET_CRYPTO_SymmetricSessionKey *skey) | ||
454 | { | ||
455 | static const char ctx[] = "aes key generation vector"; | ||
456 | |||
457 | #if DEBUG_KX | ||
458 | struct GNUNET_HashCode sh; | ||
459 | |||
460 | GNUNET_CRYPTO_hash (skey, sizeof(*skey), &sh); | ||
461 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
462 | "Deriving AES Keys for %s to %s from %s\n", | ||
463 | GNUNET_i2s (sender), | ||
464 | GNUNET_i2s2 (receiver), | ||
465 | GNUNET_h2s (key_material)); | ||
466 | #endif | ||
467 | GNUNET_CRYPTO_kdf (skey, | ||
468 | sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey), | ||
469 | ctx, | ||
470 | sizeof(ctx), | ||
471 | key_material, | ||
472 | sizeof(struct GNUNET_HashCode), | ||
473 | sender, | ||
474 | sizeof(struct GNUNET_PeerIdentity), | ||
475 | receiver, | ||
476 | sizeof(struct GNUNET_PeerIdentity), | ||
477 | NULL); | ||
478 | } | ||
479 | |||
480 | |||
481 | /** | ||
482 | * Encrypt size bytes from @a in and write the result to @a out. Use the | ||
483 | * @a kx key for outbound traffic of the given neighbour. | ||
484 | * | ||
485 | * @param kx key information context | ||
486 | * @param iv initialization vector to use | ||
487 | * @param in ciphertext | ||
488 | * @param out plaintext | ||
489 | * @param size size of @a in / @a out | ||
490 | * | ||
491 | * @return #GNUNET_OK on success | ||
492 | */ | ||
493 | static int | ||
494 | do_encrypt (struct GSC_KeyExchangeInfo *kx, | ||
495 | const struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, | ||
496 | const void *in, | ||
497 | void *out, | ||
498 | size_t size) | ||
499 | { | ||
500 | if (size != (uint16_t) size) | ||
501 | { | ||
502 | GNUNET_break (0); | ||
503 | return GNUNET_NO; | ||
504 | } | ||
505 | GNUNET_assert (size == GNUNET_CRYPTO_symmetric_encrypt (in, | ||
506 | (uint16_t) size, | ||
507 | &kx->encrypt_key, | ||
508 | iv, | ||
509 | out)); | ||
510 | GNUNET_STATISTICS_update (GSC_stats, | ||
511 | gettext_noop ("# bytes encrypted"), | ||
512 | size, | ||
513 | GNUNET_NO); | ||
514 | /* the following is too sensitive to write to log files by accident, | ||
515 | so we require manual intervention to get this one... */ | ||
516 | #if DEBUG_KX | ||
517 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
518 | "Encrypted %u bytes for `%s' using key %s, IV %u\n", | ||
519 | (unsigned int) size, | ||
520 | GNUNET_i2s (kx->peer), | ||
521 | kx->encrypt_key.aes_key, | ||
522 | GNUNET_CRYPTO_crc32_n (iv, sizeof(iv))); | ||
523 | #endif | ||
524 | return GNUNET_OK; | ||
525 | } | ||
526 | |||
527 | |||
528 | /** | ||
529 | * Decrypt size bytes from @a in and write the result to @a out. Use | ||
530 | * the @a kx key for inbound traffic of the given neighbour. This | ||
531 | * function does NOT do any integrity-checks on the result. | ||
532 | * | ||
533 | * @param kx key information context | ||
534 | * @param iv initialization vector to use | ||
535 | * @param in ciphertext | ||
536 | * @param out plaintext | ||
537 | * @param size size of @a in / @a out | ||
538 | * @return #GNUNET_OK on success | ||
539 | */ | ||
540 | static int | ||
541 | do_decrypt (struct GSC_KeyExchangeInfo *kx, | ||
542 | const struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, | ||
543 | const void *in, | ||
544 | void *out, | ||
545 | size_t size) | ||
546 | { | ||
547 | if (size != (uint16_t) size) | ||
548 | { | ||
549 | GNUNET_break (0); | ||
550 | return GNUNET_NO; | ||
551 | } | ||
552 | if ((kx->status != GNUNET_CORE_KX_STATE_KEY_RECEIVED) && | ||
553 | (kx->status != GNUNET_CORE_KX_STATE_UP) && | ||
554 | (kx->status != GNUNET_CORE_KX_STATE_REKEY_SENT)) | ||
555 | { | ||
556 | GNUNET_break_op (0); | ||
557 | return GNUNET_SYSERR; | ||
558 | } | ||
559 | if (size != GNUNET_CRYPTO_symmetric_decrypt (in, | ||
560 | (uint16_t) size, | ||
561 | &kx->decrypt_key, | ||
562 | iv, | ||
563 | out)) | ||
564 | { | ||
565 | GNUNET_break (0); | ||
566 | return GNUNET_SYSERR; | ||
567 | } | ||
568 | GNUNET_STATISTICS_update (GSC_stats, | ||
569 | gettext_noop ("# bytes decrypted"), | ||
570 | size, | ||
571 | GNUNET_NO); | ||
572 | /* the following is too sensitive to write to log files by accident, | ||
573 | so we require manual intervention to get this one... */ | ||
574 | #if DEBUG_KX | ||
575 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
576 | "Decrypted %u bytes from `%s' using key %s, IV %u\n", | ||
577 | (unsigned int) size, | ||
578 | GNUNET_i2s (kx->peer), | ||
579 | kx->decrypt_key.aes_key, | ||
580 | GNUNET_CRYPTO_crc32_n (iv, sizeof(*iv))); | ||
581 | #endif | ||
582 | return GNUNET_OK; | ||
583 | } | ||
584 | |||
585 | |||
586 | /** | ||
587 | * Send our key (and encrypted PING) to the other peer. | ||
588 | * | ||
589 | * @param kx key exchange context | ||
590 | */ | ||
591 | static void | ||
592 | send_key (struct GSC_KeyExchangeInfo *kx); | ||
593 | |||
594 | |||
595 | /** | ||
596 | * Task that will retry #send_key() if our previous attempt failed. | ||
597 | * | ||
598 | * @param cls our `struct GSC_KeyExchangeInfo` | ||
599 | */ | ||
600 | static void | ||
601 | set_key_retry_task (void *cls) | ||
602 | { | ||
603 | struct GSC_KeyExchangeInfo *kx = cls; | ||
604 | |||
605 | kx->retry_set_key_task = NULL; | ||
606 | kx->set_key_retry_frequency = | ||
607 | GNUNET_TIME_STD_BACKOFF (kx->set_key_retry_frequency); | ||
608 | GNUNET_assert (GNUNET_CORE_KX_STATE_DOWN != kx->status); | ||
609 | send_key (kx); | ||
610 | } | ||
611 | |||
612 | |||
613 | /** | ||
614 | * Create a fresh PING message for transmission to the other peer. | ||
615 | * | ||
616 | * @param kx key exchange context to create PING for | ||
617 | */ | ||
618 | static void | ||
619 | setup_fresh_ping (struct GSC_KeyExchangeInfo *kx) | ||
620 | { | ||
621 | struct PingMessage pp; | ||
622 | struct PingMessage *pm; | ||
623 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
624 | |||
625 | pm = &kx->ping; | ||
626 | kx->ping_challenge = | ||
627 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); | ||
628 | pm->header.size = htons (sizeof(struct PingMessage)); | ||
629 | pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING); | ||
630 | pm->iv_seed = calculate_seed (kx); | ||
631 | derive_iv (&iv, &kx->encrypt_key, pm->iv_seed, kx->peer); | ||
632 | pp.challenge = kx->ping_challenge; | ||
633 | pp.target = *kx->peer; | ||
634 | do_encrypt (kx, | ||
635 | &iv, | ||
636 | &pp.target, | ||
637 | &pm->target, | ||
638 | sizeof(struct PingMessage) | ||
639 | - ((void *) &pm->target - (void *) pm)); | ||
640 | } | ||
641 | |||
642 | |||
643 | /** | ||
644 | * Deliver P2P message to interested clients. Invokes send twice, | ||
645 | * once for clients that want the full message, and once for clients | ||
646 | * that only want the header | ||
647 | * | ||
648 | * @param cls the `struct GSC_KeyExchangeInfo` | ||
649 | * @param m the message | ||
650 | * @return #GNUNET_OK on success, | ||
651 | * #GNUNET_NO to stop further processing (no error) | ||
652 | * #GNUNET_SYSERR to stop further processing with error | ||
653 | */ | ||
654 | static int | ||
655 | deliver_message (void *cls, const struct GNUNET_MessageHeader *m) | ||
656 | { | ||
657 | struct GSC_KeyExchangeInfo *kx = cls; | ||
658 | |||
659 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
660 | "Decrypted message of type %d from %s\n", | ||
661 | ntohs (m->type), | ||
662 | GNUNET_i2s (kx->peer)); | ||
663 | if (GNUNET_CORE_KX_STATE_UP != kx->status) | ||
664 | { | ||
665 | GNUNET_STATISTICS_update (GSC_stats, | ||
666 | gettext_noop ("# PAYLOAD dropped (out of order)"), | ||
667 | 1, | ||
668 | GNUNET_NO); | ||
669 | return GNUNET_OK; | ||
670 | } | ||
671 | switch (ntohs (m->type)) | ||
672 | { | ||
673 | case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP: | ||
674 | case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP: | ||
675 | GSC_SESSIONS_set_typemap (kx->peer, m); | ||
676 | return GNUNET_OK; | ||
677 | |||
678 | case GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP: | ||
679 | GSC_SESSIONS_confirm_typemap (kx->peer, m); | ||
680 | return GNUNET_OK; | ||
681 | |||
682 | default: | ||
683 | GSC_CLIENTS_deliver_message (kx->peer, | ||
684 | m, | ||
685 | ntohs (m->size), | ||
686 | GNUNET_CORE_OPTION_SEND_FULL_INBOUND); | ||
687 | GSC_CLIENTS_deliver_message (kx->peer, | ||
688 | m, | ||
689 | sizeof(struct GNUNET_MessageHeader), | ||
690 | GNUNET_CORE_OPTION_SEND_HDR_INBOUND); | ||
691 | } | ||
692 | return GNUNET_OK; | ||
693 | } | ||
694 | |||
695 | |||
696 | /** | ||
697 | * Function called by transport to notify us that | ||
698 | * a peer connected to us (on the network level). | ||
699 | * Starts the key exchange with the given peer. | ||
700 | * | ||
701 | * @param cls closure (NULL) | ||
702 | * @param pid identity of the peer to do a key exchange with | ||
703 | * @return key exchange information context | ||
704 | */ | ||
705 | static void * | ||
706 | handle_transport_notify_connect (void *cls, | ||
707 | const struct GNUNET_PeerIdentity *pid, | ||
708 | struct GNUNET_MQ_Handle *mq) | ||
709 | { | ||
710 | struct GSC_KeyExchangeInfo *kx; | ||
711 | struct GNUNET_HashCode h1; | ||
712 | struct GNUNET_HashCode h2; | ||
713 | |||
714 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
715 | "Initiating key exchange with `%s'\n", | ||
716 | GNUNET_i2s (pid)); | ||
717 | GNUNET_STATISTICS_update (GSC_stats, | ||
718 | gettext_noop ("# key exchanges initiated"), | ||
719 | 1, | ||
720 | GNUNET_NO); | ||
721 | |||
722 | kx = GNUNET_new (struct GSC_KeyExchangeInfo); | ||
723 | kx->mst = GNUNET_MST_create (&deliver_message, kx); | ||
724 | kx->mq = mq; | ||
725 | kx->peer = pid; | ||
726 | kx->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY; | ||
727 | GNUNET_CONTAINER_DLL_insert (kx_head, kx_tail, kx); | ||
728 | kx->status = GNUNET_CORE_KX_STATE_KEY_SENT; | ||
729 | monitor_notify_all (kx); | ||
730 | GNUNET_CRYPTO_hash (pid, sizeof(struct GNUNET_PeerIdentity), &h1); | ||
731 | GNUNET_CRYPTO_hash (&GSC_my_identity, | ||
732 | sizeof(struct GNUNET_PeerIdentity), | ||
733 | &h2); | ||
734 | if (0 < GNUNET_CRYPTO_hash_cmp (&h1, &h2)) | ||
735 | { | ||
736 | /* peer with "lower" identity starts KX, otherwise we typically end up | ||
737 | with both peers starting the exchange and transmit the 'set key' | ||
738 | message twice */ | ||
739 | send_key (kx); | ||
740 | } | ||
741 | else | ||
742 | { | ||
743 | /* peer with "higher" identity starts a delayed KX, if the "lower" peer | ||
744 | * does not start a KX since it sees no reasons to do so */ | ||
745 | kx->retry_set_key_task = | ||
746 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, | ||
747 | &set_key_retry_task, | ||
748 | kx); | ||
749 | } | ||
750 | return kx; | ||
751 | } | ||
752 | |||
753 | |||
754 | /** | ||
755 | * Function called by transport telling us that a peer | ||
756 | * disconnected. | ||
757 | * Stop key exchange with the given peer. Clean up key material. | ||
758 | * | ||
759 | * @param cls closure | ||
760 | * @param peer the peer that disconnected | ||
761 | * @param handler_cls the `struct GSC_KeyExchangeInfo` of the peer | ||
762 | */ | ||
763 | static void | ||
764 | handle_transport_notify_disconnect (void *cls, | ||
765 | const struct GNUNET_PeerIdentity *peer, | ||
766 | void *handler_cls) | ||
767 | { | ||
768 | struct GSC_KeyExchangeInfo *kx = handler_cls; | ||
769 | |||
770 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
771 | "Peer `%s' disconnected from us.\n", | ||
772 | GNUNET_i2s (peer)); | ||
773 | GSC_SESSIONS_end (kx->peer); | ||
774 | GNUNET_STATISTICS_update (GSC_stats, | ||
775 | gettext_noop ("# key exchanges stopped"), | ||
776 | 1, | ||
777 | GNUNET_NO); | ||
778 | if (NULL != kx->retry_set_key_task) | ||
779 | { | ||
780 | GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); | ||
781 | kx->retry_set_key_task = NULL; | ||
782 | } | ||
783 | if (NULL != kx->keep_alive_task) | ||
784 | { | ||
785 | GNUNET_SCHEDULER_cancel (kx->keep_alive_task); | ||
786 | kx->keep_alive_task = NULL; | ||
787 | } | ||
788 | kx->status = GNUNET_CORE_KX_PEER_DISCONNECT; | ||
789 | monitor_notify_all (kx); | ||
790 | GNUNET_CONTAINER_DLL_remove (kx_head, kx_tail, kx); | ||
791 | GNUNET_MST_destroy (kx->mst); | ||
792 | GNUNET_free (kx); | ||
793 | } | ||
794 | |||
795 | |||
796 | /** | ||
797 | * Send our PING to the other peer. | ||
798 | * | ||
799 | * @param kx key exchange context | ||
800 | */ | ||
801 | static void | ||
802 | send_ping (struct GSC_KeyExchangeInfo *kx) | ||
803 | { | ||
804 | struct GNUNET_MQ_Envelope *env; | ||
805 | |||
806 | GNUNET_STATISTICS_update (GSC_stats, | ||
807 | gettext_noop ("# PING messages transmitted"), | ||
808 | 1, | ||
809 | GNUNET_NO); | ||
810 | env = GNUNET_MQ_msg_copy (&kx->ping.header); | ||
811 | GNUNET_MQ_send (kx->mq, env); | ||
812 | } | ||
813 | |||
814 | |||
815 | /** | ||
816 | * Derive fresh session keys from the current ephemeral keys. | ||
817 | * | ||
818 | * @param kx session to derive keys for | ||
819 | */ | ||
820 | static void | ||
821 | derive_session_keys (struct GSC_KeyExchangeInfo *kx) | ||
822 | { | ||
823 | struct GNUNET_HashCode key_material; | ||
824 | |||
825 | if (GNUNET_OK != | ||
826 | GNUNET_CRYPTO_ecc_ecdh (&my_ephemeral_key, | ||
827 | &kx->other_ephemeral_key, | ||
828 | &key_material)) | ||
829 | { | ||
830 | GNUNET_break (0); | ||
831 | return; | ||
832 | } | ||
833 | derive_aes_key (&GSC_my_identity, kx->peer, &key_material, &kx->encrypt_key); | ||
834 | derive_aes_key (kx->peer, &GSC_my_identity, &key_material, &kx->decrypt_key); | ||
835 | memset (&key_material, 0, sizeof(key_material)); | ||
836 | /* fresh key, reset sequence numbers */ | ||
837 | kx->last_sequence_number_received = 0; | ||
838 | kx->last_packets_bitmap = 0; | ||
839 | setup_fresh_ping (kx); | ||
840 | } | ||
841 | |||
842 | |||
843 | /** | ||
844 | * We received a #GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY message. | ||
845 | * Validate and update our key material and status. | ||
846 | * | ||
847 | * @param cls key exchange status for the corresponding peer | ||
848 | * @param m the set key message we received | ||
849 | */ | ||
850 | static void | ||
851 | handle_ephemeral_key (void *cls, const struct EphemeralKeyMessage *m) | ||
852 | { | ||
853 | struct GSC_KeyExchangeInfo *kx = cls; | ||
854 | struct GNUNET_TIME_Absolute start_t; | ||
855 | struct GNUNET_TIME_Absolute end_t; | ||
856 | struct GNUNET_TIME_Absolute now; | ||
857 | enum GNUNET_CORE_KxState sender_status; | ||
858 | enum GNUNET_GenericReturnValue do_verify = GNUNET_YES; | ||
859 | |||
860 | end_t = GNUNET_TIME_absolute_ntoh (m->expiration_time); | ||
861 | if (((GNUNET_CORE_KX_STATE_KEY_RECEIVED == kx->status) || | ||
862 | (GNUNET_CORE_KX_STATE_UP == kx->status) || | ||
863 | (GNUNET_CORE_KX_STATE_REKEY_SENT == kx->status)) && | ||
864 | (end_t.abs_value_us < kx->foreign_key_expires.abs_value_us)) | ||
865 | { | ||
866 | GNUNET_STATISTICS_update (GSC_stats, | ||
867 | gettext_noop ("# old ephemeral keys ignored"), | ||
868 | 1, | ||
869 | GNUNET_NO); | ||
870 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
871 | "Received expired EPHEMERAL_KEY from %s\n", | ||
872 | GNUNET_i2s (&m->origin_identity)); | ||
873 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
874 | return; | ||
875 | } | ||
876 | if (0 == memcmp (&m->ephemeral_key, | ||
877 | &kx->other_ephemeral_key, | ||
878 | sizeof(m->ephemeral_key))) | ||
879 | { | ||
880 | GNUNET_STATISTICS_update (GSC_stats, | ||
881 | gettext_noop ( | ||
882 | "# duplicate ephemeral keys. Not verifying."), | ||
883 | 1, | ||
884 | GNUNET_NO); | ||
885 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
886 | "Duplicate EPHEMERAL_KEY from %s, do not verify\n", | ||
887 | GNUNET_i2s (&m->origin_identity)); | ||
888 | do_verify = GNUNET_NO; | ||
889 | } | ||
890 | if (0 != memcmp (&m->origin_identity, | ||
891 | kx->peer, | ||
892 | sizeof(struct GNUNET_PeerIdentity))) | ||
893 | { | ||
894 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
895 | "Received EPHEMERAL_KEY from %s, but expected %s\n", | ||
896 | GNUNET_i2s (&m->origin_identity), | ||
897 | GNUNET_i2s_full (kx->peer)); | ||
898 | GNUNET_break_op (0); | ||
899 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
900 | return; | ||
901 | } | ||
902 | if (do_verify && ((ntohl (m->purpose.size) != | ||
903 | sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) | ||
904 | + sizeof(struct GNUNET_TIME_AbsoluteNBO) | ||
905 | + sizeof(struct GNUNET_TIME_AbsoluteNBO) | ||
906 | + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) | ||
907 | + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)) || | ||
908 | (GNUNET_OK != | ||
909 | GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY, | ||
910 | &m->purpose, | ||
911 | &m->signature, | ||
912 | &m->origin_identity.public_key)))) | ||
913 | { | ||
914 | /* invalid signature */ | ||
915 | GNUNET_break_op (0); | ||
916 | GNUNET_STATISTICS_update (GSC_stats, | ||
917 | gettext_noop ( | ||
918 | "# EPHEMERAL_KEYs rejected (bad signature)"), | ||
919 | 1, | ||
920 | GNUNET_NO); | ||
921 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
922 | "Received EPHEMERAL_KEY from %s with bad signature\n", | ||
923 | GNUNET_i2s (&m->origin_identity)); | ||
924 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
925 | return; | ||
926 | } | ||
927 | now = GNUNET_TIME_absolute_get (); | ||
928 | start_t = GNUNET_TIME_absolute_ntoh (m->creation_time); | ||
929 | if ((end_t.abs_value_us < | ||
930 | GNUNET_TIME_absolute_subtract (now, REKEY_TOLERANCE).abs_value_us) || | ||
931 | (start_t.abs_value_us > | ||
932 | GNUNET_TIME_absolute_add (now, REKEY_TOLERANCE).abs_value_us)) | ||
933 | { | ||
934 | GNUNET_log ( | ||
935 | GNUNET_ERROR_TYPE_WARNING, | ||
936 | _ ( | ||
937 | "EPHEMERAL_KEY from peer `%s' rejected as its validity range does not match our system time (%llu not in [%llu,%llu]).\n"), | ||
938 | GNUNET_i2s (kx->peer), | ||
939 | (unsigned long long) now.abs_value_us, | ||
940 | (unsigned long long) start_t.abs_value_us, | ||
941 | (unsigned long long) end_t.abs_value_us); | ||
942 | GNUNET_STATISTICS_update (GSC_stats, | ||
943 | gettext_noop ( | ||
944 | "# EPHEMERAL_KEY messages rejected due to time"), | ||
945 | 1, | ||
946 | GNUNET_NO); | ||
947 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
948 | return; | ||
949 | } | ||
950 | #if DEBUG_KX | ||
951 | { | ||
952 | struct GNUNET_HashCode eh; | ||
953 | |||
954 | GNUNET_CRYPTO_hash (&m->ephemeral_key, sizeof(m->ephemeral_key), &eh); | ||
955 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
956 | "Received valid EPHEMERAL_KEY `%s' from `%s' in state %d.\n", | ||
957 | GNUNET_h2s (&eh), | ||
958 | GNUNET_i2s (kx->peer), | ||
959 | kx->status); | ||
960 | } | ||
961 | #endif | ||
962 | GNUNET_STATISTICS_update (GSC_stats, | ||
963 | gettext_noop ("# valid ephemeral keys received"), | ||
964 | 1, | ||
965 | GNUNET_NO); | ||
966 | kx->other_ephemeral_key = m->ephemeral_key; | ||
967 | kx->foreign_key_expires = end_t; | ||
968 | derive_session_keys (kx); | ||
969 | |||
970 | /* check if we still need to send the sender our key */ | ||
971 | sender_status = (enum GNUNET_CORE_KxState) ntohl (m->sender_status); | ||
972 | switch (sender_status) | ||
973 | { | ||
974 | case GNUNET_CORE_KX_STATE_DOWN: | ||
975 | GNUNET_break_op (0); | ||
976 | break; | ||
977 | |||
978 | case GNUNET_CORE_KX_STATE_KEY_SENT: | ||
979 | /* fine, need to send our key after updating our status, see below */ | ||
980 | GSC_SESSIONS_reinit (kx->peer); | ||
981 | break; | ||
982 | |||
983 | case GNUNET_CORE_KX_STATE_KEY_RECEIVED: | ||
984 | /* other peer already got our key, but typemap did go down */ | ||
985 | GSC_SESSIONS_reinit (kx->peer); | ||
986 | break; | ||
987 | |||
988 | case GNUNET_CORE_KX_STATE_UP: | ||
989 | /* other peer already got our key, typemap NOT down */ | ||
990 | break; | ||
991 | |||
992 | case GNUNET_CORE_KX_STATE_REKEY_SENT: | ||
993 | /* other peer already got our key, typemap NOT down */ | ||
994 | break; | ||
995 | |||
996 | default: | ||
997 | GNUNET_break (0); | ||
998 | break; | ||
999 | } | ||
1000 | /* check if we need to confirm everything is fine via PING + PONG */ | ||
1001 | switch (kx->status) | ||
1002 | { | ||
1003 | case GNUNET_CORE_KX_STATE_DOWN: | ||
1004 | GNUNET_assert (NULL == kx->keep_alive_task); | ||
1005 | kx->status = GNUNET_CORE_KX_STATE_KEY_RECEIVED; | ||
1006 | monitor_notify_all (kx); | ||
1007 | if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) | ||
1008 | send_key (kx); | ||
1009 | else | ||
1010 | send_ping (kx); | ||
1011 | break; | ||
1012 | |||
1013 | case GNUNET_CORE_KX_STATE_KEY_SENT: | ||
1014 | GNUNET_assert (NULL == kx->keep_alive_task); | ||
1015 | kx->status = GNUNET_CORE_KX_STATE_KEY_RECEIVED; | ||
1016 | monitor_notify_all (kx); | ||
1017 | if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) | ||
1018 | send_key (kx); | ||
1019 | else | ||
1020 | send_ping (kx); | ||
1021 | break; | ||
1022 | |||
1023 | case GNUNET_CORE_KX_STATE_KEY_RECEIVED: | ||
1024 | GNUNET_assert (NULL == kx->keep_alive_task); | ||
1025 | if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) | ||
1026 | send_key (kx); | ||
1027 | else | ||
1028 | send_ping (kx); | ||
1029 | break; | ||
1030 | |||
1031 | case GNUNET_CORE_KX_STATE_UP: | ||
1032 | kx->status = GNUNET_CORE_KX_STATE_REKEY_SENT; | ||
1033 | monitor_notify_all (kx); | ||
1034 | if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) | ||
1035 | send_key (kx); | ||
1036 | else | ||
1037 | send_ping (kx); | ||
1038 | break; | ||
1039 | |||
1040 | case GNUNET_CORE_KX_STATE_REKEY_SENT: | ||
1041 | if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) | ||
1042 | send_key (kx); | ||
1043 | else | ||
1044 | send_ping (kx); | ||
1045 | break; | ||
1046 | |||
1047 | default: | ||
1048 | GNUNET_break (0); | ||
1049 | break; | ||
1050 | } | ||
1051 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1052 | } | ||
1053 | |||
1054 | |||
1055 | static void | ||
1056 | send_keep_alive (void *cls); | ||
1057 | |||
1058 | |||
1059 | /** | ||
1060 | * We received a PING message. Validate and transmit | ||
1061 | * a PONG message. | ||
1062 | * | ||
1063 | * @param cls key exchange status for the corresponding peer | ||
1064 | * @param m the encrypted PING message itself | ||
1065 | */ | ||
1066 | static void | ||
1067 | handle_ping (void *cls, const struct PingMessage *m) | ||
1068 | { | ||
1069 | struct GSC_KeyExchangeInfo *kx = cls; | ||
1070 | struct PingMessage t; | ||
1071 | struct PongMessage tx; | ||
1072 | struct PongMessage *tp; | ||
1073 | struct GNUNET_MQ_Envelope *env; | ||
1074 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
1075 | |||
1076 | GNUNET_STATISTICS_update (GSC_stats, | ||
1077 | gettext_noop ("# PING messages received"), | ||
1078 | 1, | ||
1079 | GNUNET_NO); | ||
1080 | if ((kx->status != GNUNET_CORE_KX_STATE_KEY_RECEIVED) && | ||
1081 | (kx->status != GNUNET_CORE_KX_STATE_UP) && | ||
1082 | (kx->status != GNUNET_CORE_KX_STATE_REKEY_SENT)) | ||
1083 | { | ||
1084 | /* ignore */ | ||
1085 | GNUNET_STATISTICS_update (GSC_stats, | ||
1086 | gettext_noop ( | ||
1087 | "# PING messages dropped (out of order)"), | ||
1088 | 1, | ||
1089 | GNUNET_NO); | ||
1090 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1091 | return; | ||
1092 | } | ||
1093 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1094 | "Core service receives PING request from `%s'.\n", | ||
1095 | GNUNET_i2s (kx->peer)); | ||
1096 | derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity); | ||
1097 | if (GNUNET_OK != do_decrypt (kx, | ||
1098 | &iv, | ||
1099 | &m->target, | ||
1100 | &t.target, | ||
1101 | sizeof(struct PingMessage) | ||
1102 | - ((void *) &m->target - (void *) m))) | ||
1103 | { | ||
1104 | GNUNET_break_op (0); | ||
1105 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1106 | return; | ||
1107 | } | ||
1108 | if (0 != | ||
1109 | memcmp (&t.target, &GSC_my_identity, sizeof(struct GNUNET_PeerIdentity))) | ||
1110 | { | ||
1111 | if (GNUNET_CORE_KX_STATE_REKEY_SENT != kx->status) | ||
1112 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1113 | "Decryption of PING from peer `%s' failed, PING for `%s'?\n", | ||
1114 | GNUNET_i2s (kx->peer), | ||
1115 | GNUNET_i2s2 (&t.target)); | ||
1116 | else | ||
1117 | GNUNET_log ( | ||
1118 | GNUNET_ERROR_TYPE_DEBUG, | ||
1119 | "Decryption of PING from peer `%s' failed after rekey (harmless)\n", | ||
1120 | GNUNET_i2s (kx->peer)); | ||
1121 | GNUNET_break_op (0); | ||
1122 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1123 | return; | ||
1124 | } | ||
1125 | /* construct PONG */ | ||
1126 | tx.reserved = 0; | ||
1127 | tx.challenge = t.challenge; | ||
1128 | tx.target = t.target; | ||
1129 | env = GNUNET_MQ_msg (tp, GNUNET_MESSAGE_TYPE_CORE_PONG); | ||
1130 | tp->iv_seed = calculate_seed (kx); | ||
1131 | derive_pong_iv (&iv, &kx->encrypt_key, tp->iv_seed, t.challenge, kx->peer); | ||
1132 | do_encrypt (kx, | ||
1133 | &iv, | ||
1134 | &tx.challenge, | ||
1135 | &tp->challenge, | ||
1136 | sizeof(struct PongMessage) | ||
1137 | - ((void *) &tp->challenge - (void *) tp)); | ||
1138 | GNUNET_STATISTICS_update (GSC_stats, | ||
1139 | gettext_noop ("# PONG messages created"), | ||
1140 | 1, | ||
1141 | GNUNET_NO); | ||
1142 | GNUNET_MQ_send (kx->mq, env); | ||
1143 | if (NULL != kx->keep_alive_task) | ||
1144 | { | ||
1145 | GNUNET_SCHEDULER_cancel (kx->keep_alive_task); | ||
1146 | kx->keep_alive_task = GNUNET_SCHEDULER_add_delayed (MIN_PING_FREQUENCY, &send_keep_alive, kx); | ||
1147 | } | ||
1148 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1149 | } | ||
1150 | |||
1151 | |||
1152 | /** | ||
1153 | * Task triggered when a neighbour entry is about to time out | ||
1154 | * (and we should prevent this by sending a PING). | ||
1155 | * | ||
1156 | * @param cls the `struct GSC_KeyExchangeInfo` | ||
1157 | */ | ||
1158 | static void | ||
1159 | send_keep_alive (void *cls) | ||
1160 | { | ||
1161 | struct GSC_KeyExchangeInfo *kx = cls; | ||
1162 | struct GNUNET_TIME_Relative retry; | ||
1163 | struct GNUNET_TIME_Relative left; | ||
1164 | |||
1165 | kx->keep_alive_task = NULL; | ||
1166 | left = GNUNET_TIME_absolute_get_remaining (kx->timeout); | ||
1167 | if (0 == left.rel_value_us) | ||
1168 | { | ||
1169 | GNUNET_STATISTICS_update (GSC_stats, | ||
1170 | gettext_noop ("# sessions terminated by timeout"), | ||
1171 | 1, | ||
1172 | GNUNET_NO); | ||
1173 | GSC_SESSIONS_end (kx->peer); | ||
1174 | kx->status = GNUNET_CORE_KX_STATE_KEY_SENT; | ||
1175 | monitor_notify_all (kx); | ||
1176 | send_key (kx); | ||
1177 | return; | ||
1178 | } | ||
1179 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1180 | "Sending KEEPALIVE to `%s'\n", | ||
1181 | GNUNET_i2s (kx->peer)); | ||
1182 | GNUNET_STATISTICS_update (GSC_stats, | ||
1183 | gettext_noop ("# keepalive messages sent"), | ||
1184 | 1, | ||
1185 | GNUNET_NO); | ||
1186 | setup_fresh_ping (kx); | ||
1187 | send_ping (kx); | ||
1188 | retry = GNUNET_TIME_relative_max (GNUNET_TIME_relative_divide (left, 2), | ||
1189 | MIN_PING_FREQUENCY); | ||
1190 | kx->keep_alive_task = | ||
1191 | GNUNET_SCHEDULER_add_delayed (retry, &send_keep_alive, kx); | ||
1192 | } | ||
1193 | |||
1194 | |||
1195 | /** | ||
1196 | * We've seen a valid message from the other peer. | ||
1197 | * Update the time when the session would time out | ||
1198 | * and delay sending our keep alive message further. | ||
1199 | * | ||
1200 | * @param kx key exchange where we saw activity | ||
1201 | */ | ||
1202 | static void | ||
1203 | update_timeout (struct GSC_KeyExchangeInfo *kx) | ||
1204 | { | ||
1205 | struct GNUNET_TIME_Relative delta; | ||
1206 | |||
1207 | kx->timeout = | ||
1208 | GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); | ||
1209 | delta = | ||
1210 | GNUNET_TIME_absolute_get_difference (kx->last_notify_timeout, kx->timeout); | ||
1211 | if (delta.rel_value_us > 5LL * 1000LL * 1000LL) | ||
1212 | { | ||
1213 | /* we only notify monitors about timeout changes if those | ||
1214 | are bigger than the threshold (5s) */ | ||
1215 | monitor_notify_all (kx); | ||
1216 | } | ||
1217 | if (NULL != kx->keep_alive_task) | ||
1218 | GNUNET_SCHEDULER_cancel (kx->keep_alive_task); | ||
1219 | kx->keep_alive_task = GNUNET_SCHEDULER_add_delayed ( | ||
1220 | GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2), | ||
1221 | &send_keep_alive, | ||
1222 | kx); | ||
1223 | } | ||
1224 | |||
1225 | |||
1226 | /** | ||
1227 | * We received a PONG message. Validate and update our status. | ||
1228 | * | ||
1229 | * @param kx key exchange context for the the PONG | ||
1230 | * @param m the encrypted PONG message itself | ||
1231 | */ | ||
1232 | static void | ||
1233 | handle_pong (void *cls, const struct PongMessage *m) | ||
1234 | { | ||
1235 | struct GSC_KeyExchangeInfo *kx = cls; | ||
1236 | struct PongMessage t; | ||
1237 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
1238 | |||
1239 | GNUNET_STATISTICS_update (GSC_stats, | ||
1240 | gettext_noop ("# PONG messages received"), | ||
1241 | 1, | ||
1242 | GNUNET_NO); | ||
1243 | switch (kx->status) | ||
1244 | { | ||
1245 | case GNUNET_CORE_KX_STATE_DOWN: | ||
1246 | GNUNET_STATISTICS_update (GSC_stats, | ||
1247 | gettext_noop ( | ||
1248 | "# PONG messages dropped (connection down)"), | ||
1249 | 1, | ||
1250 | GNUNET_NO); | ||
1251 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1252 | return; | ||
1253 | |||
1254 | case GNUNET_CORE_KX_STATE_KEY_SENT: | ||
1255 | GNUNET_STATISTICS_update (GSC_stats, | ||
1256 | gettext_noop ( | ||
1257 | "# PONG messages dropped (out of order)"), | ||
1258 | 1, | ||
1259 | GNUNET_NO); | ||
1260 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1261 | return; | ||
1262 | |||
1263 | case GNUNET_CORE_KX_STATE_KEY_RECEIVED: | ||
1264 | break; | ||
1265 | |||
1266 | case GNUNET_CORE_KX_STATE_UP: | ||
1267 | break; | ||
1268 | |||
1269 | case GNUNET_CORE_KX_STATE_REKEY_SENT: | ||
1270 | break; | ||
1271 | |||
1272 | default: | ||
1273 | GNUNET_break (0); | ||
1274 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1275 | return; | ||
1276 | } | ||
1277 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1278 | "Core service receives PONG response from `%s'.\n", | ||
1279 | GNUNET_i2s (kx->peer)); | ||
1280 | /* mark as garbage, just to be sure */ | ||
1281 | memset (&t, 255, sizeof(t)); | ||
1282 | derive_pong_iv (&iv, | ||
1283 | &kx->decrypt_key, | ||
1284 | m->iv_seed, | ||
1285 | kx->ping_challenge, | ||
1286 | &GSC_my_identity); | ||
1287 | if (GNUNET_OK != do_decrypt (kx, | ||
1288 | &iv, | ||
1289 | &m->challenge, | ||
1290 | &t.challenge, | ||
1291 | sizeof(struct PongMessage) | ||
1292 | - ((void *) &m->challenge - (void *) m))) | ||
1293 | { | ||
1294 | GNUNET_break_op (0); | ||
1295 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1296 | return; | ||
1297 | } | ||
1298 | GNUNET_STATISTICS_update (GSC_stats, | ||
1299 | gettext_noop ("# PONG messages decrypted"), | ||
1300 | 1, | ||
1301 | GNUNET_NO); | ||
1302 | if ((0 != | ||
1303 | memcmp (&t.target, kx->peer, sizeof(struct GNUNET_PeerIdentity))) || | ||
1304 | (kx->ping_challenge != t.challenge)) | ||
1305 | { | ||
1306 | /* PONG malformed */ | ||
1307 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1308 | "Received malformed PONG wanted sender `%s' with challenge %u\n", | ||
1309 | GNUNET_i2s (kx->peer), | ||
1310 | (unsigned int) kx->ping_challenge); | ||
1311 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1312 | "Received malformed PONG received from `%s' with challenge %u\n", | ||
1313 | GNUNET_i2s (&t.target), | ||
1314 | (unsigned int) t.challenge); | ||
1315 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1316 | return; | ||
1317 | } | ||
1318 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1319 | "Received valid PONG from `%s'\n", | ||
1320 | GNUNET_i2s (kx->peer)); | ||
1321 | /* no need to resend key any longer */ | ||
1322 | if (NULL != kx->retry_set_key_task) | ||
1323 | { | ||
1324 | GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); | ||
1325 | kx->retry_set_key_task = NULL; | ||
1326 | } | ||
1327 | switch (kx->status) | ||
1328 | { | ||
1329 | case GNUNET_CORE_KX_STATE_DOWN: | ||
1330 | GNUNET_assert (0); /* should be impossible */ | ||
1331 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1332 | return; | ||
1333 | |||
1334 | case GNUNET_CORE_KX_STATE_KEY_SENT: | ||
1335 | GNUNET_assert (0); /* should be impossible */ | ||
1336 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1337 | return; | ||
1338 | |||
1339 | case GNUNET_CORE_KX_STATE_KEY_RECEIVED: | ||
1340 | GNUNET_STATISTICS_update (GSC_stats, | ||
1341 | gettext_noop ( | ||
1342 | "# session keys confirmed via PONG"), | ||
1343 | 1, | ||
1344 | GNUNET_NO); | ||
1345 | kx->status = GNUNET_CORE_KX_STATE_UP; | ||
1346 | monitor_notify_all (kx); | ||
1347 | GSC_SESSIONS_create (kx->peer, kx); | ||
1348 | GNUNET_assert (NULL == kx->keep_alive_task); | ||
1349 | update_timeout (kx); | ||
1350 | break; | ||
1351 | |||
1352 | case GNUNET_CORE_KX_STATE_UP: | ||
1353 | GNUNET_STATISTICS_update (GSC_stats, | ||
1354 | gettext_noop ("# timeouts prevented via PONG"), | ||
1355 | 1, | ||
1356 | GNUNET_NO); | ||
1357 | update_timeout (kx); | ||
1358 | break; | ||
1359 | |||
1360 | case GNUNET_CORE_KX_STATE_REKEY_SENT: | ||
1361 | GNUNET_STATISTICS_update (GSC_stats, | ||
1362 | gettext_noop ( | ||
1363 | "# rekey operations confirmed via PONG"), | ||
1364 | 1, | ||
1365 | GNUNET_NO); | ||
1366 | kx->status = GNUNET_CORE_KX_STATE_UP; | ||
1367 | monitor_notify_all (kx); | ||
1368 | update_timeout (kx); | ||
1369 | break; | ||
1370 | |||
1371 | default: | ||
1372 | GNUNET_break (0); | ||
1373 | break; | ||
1374 | } | ||
1375 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1376 | } | ||
1377 | |||
1378 | |||
1379 | /** | ||
1380 | * Send our key to the other peer. | ||
1381 | * | ||
1382 | * @param kx key exchange context | ||
1383 | */ | ||
1384 | static void | ||
1385 | send_key (struct GSC_KeyExchangeInfo *kx) | ||
1386 | { | ||
1387 | struct GNUNET_MQ_Envelope *env; | ||
1388 | |||
1389 | GNUNET_assert (GNUNET_CORE_KX_STATE_DOWN != kx->status); | ||
1390 | if (NULL != kx->retry_set_key_task) | ||
1391 | { | ||
1392 | GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); | ||
1393 | kx->retry_set_key_task = NULL; | ||
1394 | } | ||
1395 | /* always update sender status in SET KEY message */ | ||
1396 | #if DEBUG_KX | ||
1397 | { | ||
1398 | struct GNUNET_HashCode hc; | ||
1399 | |||
1400 | GNUNET_CRYPTO_hash (¤t_ekm.ephemeral_key, | ||
1401 | sizeof(current_ekm.ephemeral_key), | ||
1402 | &hc); | ||
1403 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1404 | "Sending EPHEMERAL_KEY %s to `%s' (my status: %d)\n", | ||
1405 | GNUNET_h2s (&hc), | ||
1406 | GNUNET_i2s (kx->peer), | ||
1407 | kx->status); | ||
1408 | } | ||
1409 | #endif | ||
1410 | current_ekm.sender_status = htonl ((int32_t) (kx->status)); | ||
1411 | env = GNUNET_MQ_msg_copy (¤t_ekm.header); | ||
1412 | GNUNET_MQ_send (kx->mq, env); | ||
1413 | if (GNUNET_CORE_KX_STATE_KEY_SENT != kx->status) | ||
1414 | send_ping (kx); | ||
1415 | kx->retry_set_key_task = | ||
1416 | GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency, | ||
1417 | &set_key_retry_task, | ||
1418 | kx); | ||
1419 | } | ||
1420 | |||
1421 | |||
1422 | void | ||
1423 | GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, | ||
1424 | const void *payload, | ||
1425 | size_t payload_size) | ||
1426 | { | ||
1427 | size_t used = payload_size + sizeof(struct EncryptedMessage); | ||
1428 | char pbuf[used]; /* plaintext */ | ||
1429 | struct EncryptedMessage *em; /* encrypted message */ | ||
1430 | struct EncryptedMessage *ph; /* plaintext header */ | ||
1431 | struct GNUNET_MQ_Envelope *env; | ||
1432 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
1433 | struct GNUNET_CRYPTO_AuthKey auth_key; | ||
1434 | |||
1435 | ph = (struct EncryptedMessage *) pbuf; | ||
1436 | ph->sequence_number = htonl (++kx->last_sequence_number_sent); | ||
1437 | ph->iv_seed = calculate_seed (kx); | ||
1438 | ph->reserved = 0; | ||
1439 | ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); | ||
1440 | GNUNET_memcpy (&ph[1], payload, payload_size); | ||
1441 | env = GNUNET_MQ_msg_extra (em, | ||
1442 | payload_size, | ||
1443 | GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE); | ||
1444 | em->iv_seed = ph->iv_seed; | ||
1445 | derive_iv (&iv, &kx->encrypt_key, ph->iv_seed, kx->peer); | ||
1446 | GNUNET_assert (GNUNET_OK == do_encrypt (kx, | ||
1447 | &iv, | ||
1448 | &ph->sequence_number, | ||
1449 | &em->sequence_number, | ||
1450 | used - ENCRYPTED_HEADER_SIZE)); | ||
1451 | #if DEBUG_KX | ||
1452 | { | ||
1453 | struct GNUNET_HashCode hc; | ||
1454 | |||
1455 | GNUNET_CRYPTO_hash (&ph->sequence_number, | ||
1456 | used - ENCRYPTED_HEADER_SIZE, | ||
1457 | &hc); | ||
1458 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1459 | "Encrypted payload `%s' of %u bytes for %s\n", | ||
1460 | GNUNET_h2s (&hc), | ||
1461 | (unsigned int) (used - ENCRYPTED_HEADER_SIZE), | ||
1462 | GNUNET_i2s (kx->peer)); | ||
1463 | } | ||
1464 | #endif | ||
1465 | derive_auth_key (&auth_key, &kx->encrypt_key, ph->iv_seed); | ||
1466 | GNUNET_CRYPTO_hmac (&auth_key, | ||
1467 | &em->sequence_number, | ||
1468 | used - ENCRYPTED_HEADER_SIZE, | ||
1469 | &em->hmac); | ||
1470 | #if DEBUG_KX | ||
1471 | { | ||
1472 | struct GNUNET_HashCode hc; | ||
1473 | |||
1474 | GNUNET_CRYPTO_hash (&auth_key, sizeof(auth_key), &hc); | ||
1475 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1476 | "For peer %s, used AC %s to create hmac %s\n", | ||
1477 | GNUNET_i2s (kx->peer), | ||
1478 | GNUNET_h2s (&hc), | ||
1479 | GNUNET_h2s2 (&em->hmac)); | ||
1480 | } | ||
1481 | #endif | ||
1482 | kx->has_excess_bandwidth = GNUNET_NO; | ||
1483 | GNUNET_MQ_send (kx->mq, env); | ||
1484 | } | ||
1485 | |||
1486 | |||
1487 | /** | ||
1488 | * We received an encrypted message. Check that it is | ||
1489 | * well-formed (size-wise). | ||
1490 | * | ||
1491 | * @param cls key exchange context for encrypting the message | ||
1492 | * @param m encrypted message | ||
1493 | * @return #GNUNET_OK if @a msg is well-formed (size-wise) | ||
1494 | */ | ||
1495 | static int | ||
1496 | check_encrypted (void *cls, const struct EncryptedMessage *m) | ||
1497 | { | ||
1498 | uint16_t size = ntohs (m->header.size) - sizeof(*m); | ||
1499 | |||
1500 | if (size < sizeof(struct GNUNET_MessageHeader)) | ||
1501 | { | ||
1502 | GNUNET_break_op (0); | ||
1503 | return GNUNET_SYSERR; | ||
1504 | } | ||
1505 | return GNUNET_OK; | ||
1506 | } | ||
1507 | |||
1508 | |||
1509 | /** | ||
1510 | * We received an encrypted message. Decrypt, validate and | ||
1511 | * pass on to the appropriate clients. | ||
1512 | * | ||
1513 | * @param cls key exchange context for encrypting the message | ||
1514 | * @param m encrypted message | ||
1515 | */ | ||
1516 | static void | ||
1517 | handle_encrypted (void *cls, const struct EncryptedMessage *m) | ||
1518 | { | ||
1519 | struct GSC_KeyExchangeInfo *kx = cls; | ||
1520 | struct EncryptedMessage *pt; /* plaintext */ | ||
1521 | struct GNUNET_HashCode ph; | ||
1522 | uint32_t snum; | ||
1523 | struct GNUNET_TIME_Absolute t; | ||
1524 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
1525 | struct GNUNET_CRYPTO_AuthKey auth_key; | ||
1526 | uint16_t size = ntohs (m->header.size); | ||
1527 | char buf[size] GNUNET_ALIGN; | ||
1528 | |||
1529 | if (GNUNET_CORE_KX_STATE_UP != kx->status) | ||
1530 | { | ||
1531 | GNUNET_STATISTICS_update (GSC_stats, | ||
1532 | gettext_noop ( | ||
1533 | "# DATA message dropped (out of order)"), | ||
1534 | 1, | ||
1535 | GNUNET_NO); | ||
1536 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1537 | return; | ||
1538 | } | ||
1539 | if (0 == | ||
1540 | GNUNET_TIME_absolute_get_remaining (kx->foreign_key_expires).rel_value_us) | ||
1541 | { | ||
1542 | GNUNET_log ( | ||
1543 | GNUNET_ERROR_TYPE_WARNING, | ||
1544 | _ ( | ||
1545 | "Session to peer `%s' went down due to key expiration (should not happen)\n"), | ||
1546 | GNUNET_i2s (kx->peer)); | ||
1547 | GNUNET_STATISTICS_update (GSC_stats, | ||
1548 | gettext_noop ( | ||
1549 | "# sessions terminated by key expiration"), | ||
1550 | 1, | ||
1551 | GNUNET_NO); | ||
1552 | GSC_SESSIONS_end (kx->peer); | ||
1553 | if (NULL != kx->keep_alive_task) | ||
1554 | { | ||
1555 | GNUNET_SCHEDULER_cancel (kx->keep_alive_task); | ||
1556 | kx->keep_alive_task = NULL; | ||
1557 | } | ||
1558 | kx->status = GNUNET_CORE_KX_STATE_KEY_SENT; | ||
1559 | monitor_notify_all (kx); | ||
1560 | send_key (kx); | ||
1561 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1562 | return; | ||
1563 | } | ||
1564 | |||
1565 | /* validate hash */ | ||
1566 | #if DEBUG_KX | ||
1567 | { | ||
1568 | struct GNUNET_HashCode hc; | ||
1569 | |||
1570 | GNUNET_CRYPTO_hash (&m->sequence_number, size - ENCRYPTED_HEADER_SIZE, &hc); | ||
1571 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1572 | "Received encrypted payload `%s' of %u bytes from %s\n", | ||
1573 | GNUNET_h2s (&hc), | ||
1574 | (unsigned int) (size - ENCRYPTED_HEADER_SIZE), | ||
1575 | GNUNET_i2s (kx->peer)); | ||
1576 | } | ||
1577 | #endif | ||
1578 | derive_auth_key (&auth_key, &kx->decrypt_key, m->iv_seed); | ||
1579 | GNUNET_CRYPTO_hmac (&auth_key, | ||
1580 | &m->sequence_number, | ||
1581 | size - ENCRYPTED_HEADER_SIZE, | ||
1582 | &ph); | ||
1583 | #if DEBUG_KX | ||
1584 | { | ||
1585 | struct GNUNET_HashCode hc; | ||
1586 | |||
1587 | GNUNET_CRYPTO_hash (&auth_key, sizeof(auth_key), &hc); | ||
1588 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1589 | "For peer %s, used AC %s to verify hmac %s\n", | ||
1590 | GNUNET_i2s (kx->peer), | ||
1591 | GNUNET_h2s (&hc), | ||
1592 | GNUNET_h2s2 (&m->hmac)); | ||
1593 | } | ||
1594 | #endif | ||
1595 | if (0 != memcmp (&ph, &m->hmac, sizeof(struct GNUNET_HashCode))) | ||
1596 | { | ||
1597 | /* checksum failed */ | ||
1598 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1599 | "Failed checksum validation for a message from `%s'\n", | ||
1600 | GNUNET_i2s (kx->peer)); | ||
1601 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1602 | return; | ||
1603 | } | ||
1604 | derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity); | ||
1605 | /* decrypt */ | ||
1606 | if (GNUNET_OK != do_decrypt (kx, | ||
1607 | &iv, | ||
1608 | &m->sequence_number, | ||
1609 | &buf[ENCRYPTED_HEADER_SIZE], | ||
1610 | size - ENCRYPTED_HEADER_SIZE)) | ||
1611 | { | ||
1612 | GNUNET_break_op (0); | ||
1613 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1614 | return; | ||
1615 | } | ||
1616 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1617 | "Decrypted %u bytes from %s\n", | ||
1618 | (unsigned int) (size - ENCRYPTED_HEADER_SIZE), | ||
1619 | GNUNET_i2s (kx->peer)); | ||
1620 | pt = (struct EncryptedMessage *) buf; | ||
1621 | |||
1622 | /* validate sequence number */ | ||
1623 | snum = ntohl (pt->sequence_number); | ||
1624 | if (kx->last_sequence_number_received == snum) | ||
1625 | { | ||
1626 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1627 | "Received duplicate message, ignoring.\n"); | ||
1628 | /* duplicate, ignore */ | ||
1629 | GNUNET_STATISTICS_update (GSC_stats, | ||
1630 | gettext_noop ("# bytes dropped (duplicates)"), | ||
1631 | size, | ||
1632 | GNUNET_NO); | ||
1633 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1634 | return; | ||
1635 | } | ||
1636 | if ((kx->last_sequence_number_received > snum) && | ||
1637 | (kx->last_sequence_number_received - snum > 32)) | ||
1638 | { | ||
1639 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1640 | "Received ancient out of sequence message, ignoring.\n"); | ||
1641 | /* ancient out of sequence, ignore */ | ||
1642 | GNUNET_STATISTICS_update (GSC_stats, | ||
1643 | gettext_noop ( | ||
1644 | "# bytes dropped (out of sequence)"), | ||
1645 | size, | ||
1646 | GNUNET_NO); | ||
1647 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1648 | return; | ||
1649 | } | ||
1650 | if (kx->last_sequence_number_received > snum) | ||
1651 | { | ||
1652 | uint32_t rotbit = 1U << (kx->last_sequence_number_received - snum - 1); | ||
1653 | |||
1654 | if ((kx->last_packets_bitmap & rotbit) != 0) | ||
1655 | { | ||
1656 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1657 | "Received duplicate message, ignoring.\n"); | ||
1658 | GNUNET_STATISTICS_update (GSC_stats, | ||
1659 | gettext_noop ("# bytes dropped (duplicates)"), | ||
1660 | size, | ||
1661 | GNUNET_NO); | ||
1662 | /* duplicate, ignore */ | ||
1663 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1664 | return; | ||
1665 | } | ||
1666 | kx->last_packets_bitmap |= rotbit; | ||
1667 | } | ||
1668 | if (kx->last_sequence_number_received < snum) | ||
1669 | { | ||
1670 | unsigned int shift = (snum - kx->last_sequence_number_received); | ||
1671 | |||
1672 | if (shift >= 8 * sizeof(kx->last_packets_bitmap)) | ||
1673 | kx->last_packets_bitmap = 0; | ||
1674 | else | ||
1675 | kx->last_packets_bitmap <<= shift; | ||
1676 | kx->last_sequence_number_received = snum; | ||
1677 | } | ||
1678 | |||
1679 | /* check timestamp */ | ||
1680 | t = GNUNET_TIME_absolute_ntoh (pt->timestamp); | ||
1681 | if (GNUNET_TIME_absolute_get_duration (t).rel_value_us > | ||
1682 | MAX_MESSAGE_AGE.rel_value_us) | ||
1683 | { | ||
1684 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1685 | "Message received far too old (%s). Content ignored.\n", | ||
1686 | GNUNET_STRINGS_relative_time_to_string ( | ||
1687 | GNUNET_TIME_absolute_get_duration (t), | ||
1688 | GNUNET_YES)); | ||
1689 | GNUNET_STATISTICS_update (GSC_stats, | ||
1690 | gettext_noop ( | ||
1691 | "# bytes dropped (ancient message)"), | ||
1692 | size, | ||
1693 | GNUNET_NO); | ||
1694 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1695 | return; | ||
1696 | } | ||
1697 | |||
1698 | /* process decrypted message(s) */ | ||
1699 | update_timeout (kx); | ||
1700 | GNUNET_STATISTICS_update (GSC_stats, | ||
1701 | gettext_noop ("# bytes of payload decrypted"), | ||
1702 | size - sizeof(struct EncryptedMessage), | ||
1703 | GNUNET_NO); | ||
1704 | if (GNUNET_OK != | ||
1705 | GNUNET_MST_from_buffer (kx->mst, | ||
1706 | &buf[sizeof(struct EncryptedMessage)], | ||
1707 | size - sizeof(struct EncryptedMessage), | ||
1708 | GNUNET_YES, | ||
1709 | GNUNET_NO)) | ||
1710 | GNUNET_break_op (0); | ||
1711 | |||
1712 | GNUNET_TRANSPORT_core_receive_continue (transport, kx->peer); | ||
1713 | } | ||
1714 | |||
1715 | |||
1716 | /** | ||
1717 | * Setup the message that links the ephemeral key to our persistent | ||
1718 | * public key and generate the appropriate signature. | ||
1719 | */ | ||
1720 | static void | ||
1721 | sign_ephemeral_key () | ||
1722 | { | ||
1723 | current_ekm.header.size = htons (sizeof(struct EphemeralKeyMessage)); | ||
1724 | current_ekm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY); | ||
1725 | current_ekm.sender_status = 0; /* to be set later */ | ||
1726 | current_ekm.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY); | ||
1727 | current_ekm.purpose.size = | ||
1728 | htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) | ||
1729 | + sizeof(struct GNUNET_TIME_AbsoluteNBO) | ||
1730 | + sizeof(struct GNUNET_TIME_AbsoluteNBO) | ||
1731 | + sizeof(struct GNUNET_CRYPTO_EcdhePublicKey) | ||
1732 | + sizeof(struct GNUNET_PeerIdentity)); | ||
1733 | current_ekm.creation_time = | ||
1734 | GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); | ||
1735 | if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (GSC_cfg, | ||
1736 | "core", | ||
1737 | "USE_EPHEMERAL_KEYS")) | ||
1738 | { | ||
1739 | current_ekm.expiration_time = | ||
1740 | GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute ( | ||
1741 | GNUNET_TIME_relative_add (REKEY_FREQUENCY, | ||
1742 | REKEY_TOLERANCE))); | ||
1743 | } | ||
1744 | else | ||
1745 | { | ||
1746 | current_ekm.expiration_time = | ||
1747 | GNUNET_TIME_absolute_hton (GNUNET_TIME_UNIT_FOREVER_ABS); | ||
1748 | } | ||
1749 | GNUNET_CRYPTO_ecdhe_key_get_public (&my_ephemeral_key, | ||
1750 | ¤t_ekm.ephemeral_key); | ||
1751 | current_ekm.origin_identity = GSC_my_identity; | ||
1752 | GNUNET_assert (GNUNET_OK == | ||
1753 | GNUNET_CRYPTO_eddsa_sign_ (&my_private_key, | ||
1754 | ¤t_ekm.purpose, | ||
1755 | ¤t_ekm.signature)); | ||
1756 | } | ||
1757 | |||
1758 | |||
1759 | /** | ||
1760 | * Task run to trigger rekeying. | ||
1761 | * | ||
1762 | * @param cls closure, NULL | ||
1763 | */ | ||
1764 | static void | ||
1765 | do_rekey (void *cls) | ||
1766 | { | ||
1767 | struct GSC_KeyExchangeInfo *pos; | ||
1768 | |||
1769 | (void) cls; | ||
1770 | rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY, &do_rekey, NULL); | ||
1771 | GNUNET_CRYPTO_ecdhe_key_create (&my_ephemeral_key); | ||
1772 | sign_ephemeral_key (); | ||
1773 | { | ||
1774 | struct GNUNET_HashCode eh; | ||
1775 | |||
1776 | GNUNET_CRYPTO_hash (¤t_ekm.ephemeral_key, | ||
1777 | sizeof(current_ekm.ephemeral_key), | ||
1778 | &eh); | ||
1779 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Rekeying to %s\n", GNUNET_h2s (&eh)); | ||
1780 | } | ||
1781 | for (pos = kx_head; NULL != pos; pos = pos->next) | ||
1782 | { | ||
1783 | if (GNUNET_CORE_KX_STATE_UP == pos->status) | ||
1784 | { | ||
1785 | pos->status = GNUNET_CORE_KX_STATE_REKEY_SENT; | ||
1786 | derive_session_keys (pos); | ||
1787 | } | ||
1788 | else if (GNUNET_CORE_KX_STATE_DOWN == pos->status) | ||
1789 | { | ||
1790 | pos->status = GNUNET_CORE_KX_STATE_KEY_SENT; | ||
1791 | } | ||
1792 | monitor_notify_all (pos); | ||
1793 | send_key (pos); | ||
1794 | } | ||
1795 | } | ||
1796 | |||
1797 | |||
1798 | /** | ||
1799 | * Initialize KX subsystem. | ||
1800 | * | ||
1801 | * @param pk private key to use for the peer | ||
1802 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
1803 | */ | ||
1804 | int | ||
1805 | GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk) | ||
1806 | { | ||
1807 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
1808 | GNUNET_MQ_hd_fixed_size (ephemeral_key, | ||
1809 | GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY, | ||
1810 | struct EphemeralKeyMessage, | ||
1811 | NULL), | ||
1812 | GNUNET_MQ_hd_fixed_size (ping, | ||
1813 | GNUNET_MESSAGE_TYPE_CORE_PING, | ||
1814 | struct PingMessage, | ||
1815 | NULL), | ||
1816 | GNUNET_MQ_hd_fixed_size (pong, | ||
1817 | GNUNET_MESSAGE_TYPE_CORE_PONG, | ||
1818 | struct PongMessage, | ||
1819 | NULL), | ||
1820 | GNUNET_MQ_hd_var_size (encrypted, | ||
1821 | GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE, | ||
1822 | struct EncryptedMessage, | ||
1823 | NULL), | ||
1824 | GNUNET_MQ_handler_end () | ||
1825 | }; | ||
1826 | |||
1827 | my_private_key = *pk; | ||
1828 | GNUNET_CRYPTO_eddsa_key_get_public (&my_private_key, | ||
1829 | &GSC_my_identity.public_key); | ||
1830 | GNUNET_CRYPTO_ecdhe_key_create (&my_ephemeral_key); | ||
1831 | sign_ephemeral_key (); | ||
1832 | { | ||
1833 | struct GNUNET_HashCode eh; | ||
1834 | |||
1835 | GNUNET_CRYPTO_hash (¤t_ekm.ephemeral_key, | ||
1836 | sizeof(current_ekm.ephemeral_key), | ||
1837 | &eh); | ||
1838 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1839 | "Starting with ephemeral key %s\n", | ||
1840 | GNUNET_h2s (&eh)); | ||
1841 | } | ||
1842 | |||
1843 | nc = GNUNET_notification_context_create (1); | ||
1844 | rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY, &do_rekey, NULL); | ||
1845 | transport = | ||
1846 | GNUNET_TRANSPORT_core_connect (GSC_cfg, | ||
1847 | &GSC_my_identity, | ||
1848 | handlers, | ||
1849 | NULL, | ||
1850 | &handle_transport_notify_connect, | ||
1851 | &handle_transport_notify_disconnect); | ||
1852 | if (NULL == transport) | ||
1853 | { | ||
1854 | GSC_KX_done (); | ||
1855 | return GNUNET_SYSERR; | ||
1856 | } | ||
1857 | return GNUNET_OK; | ||
1858 | } | ||
1859 | |||
1860 | |||
1861 | /** | ||
1862 | * Shutdown KX subsystem. | ||
1863 | */ | ||
1864 | void | ||
1865 | GSC_KX_done () | ||
1866 | { | ||
1867 | if (NULL != transport) | ||
1868 | { | ||
1869 | GNUNET_TRANSPORT_core_disconnect (transport); | ||
1870 | transport = NULL; | ||
1871 | } | ||
1872 | if (NULL != rekey_task) | ||
1873 | { | ||
1874 | GNUNET_SCHEDULER_cancel (rekey_task); | ||
1875 | rekey_task = NULL; | ||
1876 | } | ||
1877 | memset (&my_ephemeral_key, | ||
1878 | 0, | ||
1879 | sizeof (my_ephemeral_key)); | ||
1880 | memset (&my_private_key, | ||
1881 | 0, | ||
1882 | sizeof (my_private_key)); | ||
1883 | if (NULL != nc) | ||
1884 | { | ||
1885 | GNUNET_notification_context_destroy (nc); | ||
1886 | nc = NULL; | ||
1887 | } | ||
1888 | } | ||
1889 | |||
1890 | |||
1891 | /** | ||
1892 | * Check how many messages are queued for the given neighbour. | ||
1893 | * | ||
1894 | * @param kxinfo data about neighbour to check | ||
1895 | * @return number of items in the message queue | ||
1896 | */ | ||
1897 | unsigned int | ||
1898 | GSC_NEIGHBOURS_get_queue_length (const struct GSC_KeyExchangeInfo *kxinfo) | ||
1899 | { | ||
1900 | return GNUNET_MQ_get_length (kxinfo->mq); | ||
1901 | } | ||
1902 | |||
1903 | |||
1904 | int | ||
1905 | GSC_NEIGHBOURS_check_excess_bandwidth (const struct GSC_KeyExchangeInfo *kxinfo) | ||
1906 | { | ||
1907 | return kxinfo->has_excess_bandwidth; | ||
1908 | } | ||
1909 | |||
1910 | |||
1911 | /** | ||
1912 | * Handle #GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request. For this | ||
1913 | * request type, the client does not have to have transmitted an INIT | ||
1914 | * request. All current peers are returned, regardless of which | ||
1915 | * message types they accept. | ||
1916 | * | ||
1917 | * @param mq message queue to add for monitoring | ||
1918 | */ | ||
1919 | void | ||
1920 | GSC_KX_handle_client_monitor_peers (struct GNUNET_MQ_Handle *mq) | ||
1921 | { | ||
1922 | struct GNUNET_MQ_Envelope *env; | ||
1923 | struct MonitorNotifyMessage *done_msg; | ||
1924 | struct GSC_KeyExchangeInfo *kx; | ||
1925 | |||
1926 | GNUNET_notification_context_add (nc, mq); | ||
1927 | for (kx = kx_head; NULL != kx; kx = kx->next) | ||
1928 | { | ||
1929 | struct GNUNET_MQ_Envelope *env; | ||
1930 | struct MonitorNotifyMessage *msg; | ||
1931 | |||
1932 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY); | ||
1933 | msg->state = htonl ((uint32_t) kx->status); | ||
1934 | msg->peer = *kx->peer; | ||
1935 | msg->timeout = GNUNET_TIME_absolute_hton (kx->timeout); | ||
1936 | GNUNET_MQ_send (mq, env); | ||
1937 | } | ||
1938 | env = GNUNET_MQ_msg (done_msg, GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY); | ||
1939 | done_msg->state = htonl ((uint32_t) GNUNET_CORE_KX_ITERATION_FINISHED); | ||
1940 | done_msg->timeout = GNUNET_TIME_absolute_hton (GNUNET_TIME_UNIT_FOREVER_ABS); | ||
1941 | GNUNET_MQ_send (mq, env); | ||
1942 | } | ||
1943 | |||
1944 | |||
1945 | /* end of gnunet-service-core_kx.c */ | ||
diff --git a/src/service/core/gnunet-service-core_kx.h b/src/service/core/gnunet-service-core_kx.h new file mode 100644 index 000000000..8bcac3f68 --- /dev/null +++ b/src/service/core/gnunet-service-core_kx.h | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2011 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core_kx.h | ||
23 | * @brief code for managing the key exchange (SET_KEY, PING, PONG) with other peers | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef GNUNET_SERVICE_CORE_KX_H | ||
27 | #define GNUNET_SERVICE_CORE_KX_H | ||
28 | |||
29 | #include "gnunet_util_lib.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Information about the status of a key exchange with another peer. | ||
34 | */ | ||
35 | struct GSC_KeyExchangeInfo; | ||
36 | |||
37 | |||
38 | /** | ||
39 | * Encrypt and transmit a message with the given payload. | ||
40 | * | ||
41 | * @param kx key exchange context | ||
42 | * @param payload payload of the message | ||
43 | * @param payload_size number of bytes in 'payload' | ||
44 | */ | ||
45 | void | ||
46 | GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, | ||
47 | const void *payload, | ||
48 | size_t payload_size); | ||
49 | |||
50 | |||
51 | /** | ||
52 | * Initialize KX subsystem. | ||
53 | * | ||
54 | * @param pk private key to use for the peer | ||
55 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
56 | */ | ||
57 | int | ||
58 | GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk); | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Shutdown KX subsystem. | ||
63 | */ | ||
64 | void | ||
65 | GSC_KX_done (void); | ||
66 | |||
67 | |||
68 | /** | ||
69 | * Check if the given neighbour has excess bandwidth available. | ||
70 | * | ||
71 | * @param target neighbour to check | ||
72 | * @return #GNUNET_YES if excess bandwidth is available, #GNUNET_NO if not | ||
73 | */ | ||
74 | int | ||
75 | GSC_NEIGHBOURS_check_excess_bandwidth (const struct | ||
76 | GSC_KeyExchangeInfo *target); | ||
77 | |||
78 | |||
79 | /** | ||
80 | * Check how many messages are queued for the given neighbour. | ||
81 | * | ||
82 | * @param target neighbour to check | ||
83 | * @return number of items in the message queue | ||
84 | */ | ||
85 | unsigned int | ||
86 | GSC_NEIGHBOURS_get_queue_length (const struct GSC_KeyExchangeInfo *target); | ||
87 | |||
88 | |||
89 | /** | ||
90 | * Handle #GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request. For this | ||
91 | * request type, the client does not have to have transmitted an INIT | ||
92 | * request. All current peers are returned, regardless of which | ||
93 | * message types they accept. | ||
94 | * | ||
95 | * @param mq message queue to add for monitoring | ||
96 | */ | ||
97 | void | ||
98 | GSC_KX_handle_client_monitor_peers (struct GNUNET_MQ_Handle *mq); | ||
99 | |||
100 | |||
101 | #endif | ||
102 | /* end of gnunet-service-core_kx.h */ | ||
diff --git a/src/service/core/gnunet-service-core_sessions.c b/src/service/core/gnunet-service-core_sessions.c new file mode 100644 index 000000000..e103c89f5 --- /dev/null +++ b/src/service/core/gnunet-service-core_sessions.c | |||
@@ -0,0 +1,1028 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009-2014, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core_sessions.c | ||
23 | * @brief code for managing of 'encrypted' sessions (key exchange done) | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet-service-core.h" | ||
28 | #include "gnunet-service-core_kx.h" | ||
29 | #include "gnunet-service-core_typemap.h" | ||
30 | #include "gnunet-service-core_sessions.h" | ||
31 | #include "gnunet_constants.h" | ||
32 | #include "core.h" | ||
33 | |||
34 | |||
35 | /** | ||
36 | * How many encrypted messages do we queue at most? | ||
37 | * Needed to bound memory consumption. | ||
38 | */ | ||
39 | #define MAX_ENCRYPTED_MESSAGE_QUEUE_SIZE 4 | ||
40 | |||
41 | |||
42 | /** | ||
43 | * Message ready for encryption. This struct is followed by the | ||
44 | * actual content of the message. | ||
45 | */ | ||
46 | struct SessionMessageEntry | ||
47 | { | ||
48 | /** | ||
49 | * We keep messages in a doubly linked list. | ||
50 | */ | ||
51 | struct SessionMessageEntry *next; | ||
52 | |||
53 | /** | ||
54 | * We keep messages in a doubly linked list. | ||
55 | */ | ||
56 | struct SessionMessageEntry *prev; | ||
57 | |||
58 | /** | ||
59 | * How important is this message. | ||
60 | */ | ||
61 | enum GNUNET_MQ_PriorityPreferences priority; | ||
62 | |||
63 | /** | ||
64 | * Flag set to #GNUNET_YES if this is a typemap message. | ||
65 | */ | ||
66 | int is_typemap; | ||
67 | |||
68 | /** | ||
69 | * Flag set to #GNUNET_YES if this is a typemap confirmation message. | ||
70 | */ | ||
71 | int is_typemap_confirm; | ||
72 | |||
73 | /** | ||
74 | * Deadline for transmission, 1s after we received it (if we | ||
75 | * are not corking), otherwise "now". Note that this message | ||
76 | * does NOT expire past its deadline. | ||
77 | */ | ||
78 | struct GNUNET_TIME_Absolute deadline; | ||
79 | |||
80 | /** | ||
81 | * How long is the message? (number of bytes following the `struct | ||
82 | * MessageEntry`, but not including the size of `struct | ||
83 | * MessageEntry` itself!) | ||
84 | */ | ||
85 | size_t size; | ||
86 | }; | ||
87 | |||
88 | |||
89 | /** | ||
90 | * Data kept per session. | ||
91 | */ | ||
92 | struct Session | ||
93 | { | ||
94 | /** | ||
95 | * Identity of the other peer. | ||
96 | */ | ||
97 | const struct GNUNET_PeerIdentity *peer; | ||
98 | |||
99 | /** | ||
100 | * Key exchange state for this peer. | ||
101 | */ | ||
102 | struct GSC_KeyExchangeInfo *kx; | ||
103 | |||
104 | /** | ||
105 | * Head of list of requests from clients for transmission to | ||
106 | * this peer. | ||
107 | */ | ||
108 | struct GSC_ClientActiveRequest *active_client_request_head; | ||
109 | |||
110 | /** | ||
111 | * Tail of list of requests from clients for transmission to | ||
112 | * this peer. | ||
113 | */ | ||
114 | struct GSC_ClientActiveRequest *active_client_request_tail; | ||
115 | |||
116 | /** | ||
117 | * Head of list of messages ready for encryption. | ||
118 | */ | ||
119 | struct SessionMessageEntry *sme_head; | ||
120 | |||
121 | /** | ||
122 | * Tail of list of messages ready for encryption. | ||
123 | */ | ||
124 | struct SessionMessageEntry *sme_tail; | ||
125 | |||
126 | /** | ||
127 | * Current type map for this peer. | ||
128 | */ | ||
129 | struct GSC_TypeMap *tmap; | ||
130 | |||
131 | /** | ||
132 | * Task to transmit corked messages with a delay. | ||
133 | */ | ||
134 | struct GNUNET_SCHEDULER_Task *cork_task; | ||
135 | |||
136 | /** | ||
137 | * Task to transmit our type map. | ||
138 | */ | ||
139 | struct GNUNET_SCHEDULER_Task *typemap_task; | ||
140 | |||
141 | /** | ||
142 | * Retransmission delay we currently use for the typemap | ||
143 | * transmissions (if not confirmed). | ||
144 | */ | ||
145 | struct GNUNET_TIME_Relative typemap_delay; | ||
146 | |||
147 | /** | ||
148 | * Is this the first time we're sending the typemap? If so, | ||
149 | * we want to send it a bit faster the second time. 0 if | ||
150 | * we are sending for the first time, 1 if not. | ||
151 | */ | ||
152 | int first_typemap; | ||
153 | }; | ||
154 | |||
155 | |||
156 | GNUNET_NETWORK_STRUCT_BEGIN | ||
157 | |||
158 | /** | ||
159 | * Message sent to confirm that a typemap was received. | ||
160 | */ | ||
161 | struct TypeMapConfirmationMessage | ||
162 | { | ||
163 | /** | ||
164 | * Header with type #GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP. | ||
165 | */ | ||
166 | struct GNUNET_MessageHeader header; | ||
167 | |||
168 | /** | ||
169 | * Reserved, always zero. | ||
170 | */ | ||
171 | uint32_t reserved GNUNET_PACKED; | ||
172 | |||
173 | /** | ||
174 | * Hash of the (decompressed) type map that was received. | ||
175 | */ | ||
176 | struct GNUNET_HashCode tm_hash; | ||
177 | }; | ||
178 | |||
179 | GNUNET_NETWORK_STRUCT_END | ||
180 | |||
181 | |||
182 | /** | ||
183 | * Map of peer identities to `struct Session`. | ||
184 | */ | ||
185 | static struct GNUNET_CONTAINER_MultiPeerMap *sessions; | ||
186 | |||
187 | |||
188 | /** | ||
189 | * Find the session for the given peer. | ||
190 | * | ||
191 | * @param peer identity of the peer | ||
192 | * @return NULL if we are not connected, otherwise the | ||
193 | * session handle | ||
194 | */ | ||
195 | static struct Session * | ||
196 | find_session (const struct GNUNET_PeerIdentity *peer) | ||
197 | { | ||
198 | if (NULL == sessions) | ||
199 | return NULL; | ||
200 | return GNUNET_CONTAINER_multipeermap_get (sessions, peer); | ||
201 | } | ||
202 | |||
203 | |||
204 | /** | ||
205 | * End the session with the given peer (we are no longer | ||
206 | * connected). | ||
207 | * | ||
208 | * @param pid identity of peer to kill session with | ||
209 | */ | ||
210 | void | ||
211 | GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid) | ||
212 | { | ||
213 | struct Session *session; | ||
214 | struct GSC_ClientActiveRequest *car; | ||
215 | struct SessionMessageEntry *sme; | ||
216 | |||
217 | session = find_session (pid); | ||
218 | if (NULL == session) | ||
219 | return; | ||
220 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
221 | "Destroying session for peer `%s'\n", | ||
222 | GNUNET_i2s (session->peer)); | ||
223 | if (NULL != session->cork_task) | ||
224 | { | ||
225 | GNUNET_SCHEDULER_cancel (session->cork_task); | ||
226 | session->cork_task = NULL; | ||
227 | } | ||
228 | while (NULL != (car = session->active_client_request_head)) | ||
229 | { | ||
230 | GNUNET_CONTAINER_DLL_remove (session->active_client_request_head, | ||
231 | session->active_client_request_tail, | ||
232 | car); | ||
233 | GSC_CLIENTS_reject_request (car, GNUNET_NO); | ||
234 | } | ||
235 | while (NULL != (sme = session->sme_head)) | ||
236 | { | ||
237 | GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme); | ||
238 | GNUNET_free (sme); | ||
239 | } | ||
240 | if (NULL != session->typemap_task) | ||
241 | { | ||
242 | GNUNET_SCHEDULER_cancel (session->typemap_task); | ||
243 | session->typemap_task = NULL; | ||
244 | } | ||
245 | GSC_CLIENTS_notify_clients_about_neighbour (session->peer, | ||
246 | session->tmap, | ||
247 | NULL); | ||
248 | GNUNET_assert ( | ||
249 | GNUNET_YES == | ||
250 | GNUNET_CONTAINER_multipeermap_remove (sessions, session->peer, session)); | ||
251 | GNUNET_STATISTICS_set (GSC_stats, | ||
252 | gettext_noop ("# peers connected"), | ||
253 | GNUNET_CONTAINER_multipeermap_size (sessions), | ||
254 | GNUNET_NO); | ||
255 | GSC_TYPEMAP_destroy (session->tmap); | ||
256 | session->tmap = NULL; | ||
257 | GNUNET_free (session); | ||
258 | } | ||
259 | |||
260 | |||
261 | /** | ||
262 | * Transmit our current typemap message to the other peer. | ||
263 | * (Done periodically until the typemap is confirmed). | ||
264 | * | ||
265 | * @param cls the `struct Session *` | ||
266 | */ | ||
267 | static void | ||
268 | transmit_typemap_task (void *cls) | ||
269 | { | ||
270 | struct Session *session = cls; | ||
271 | struct GNUNET_MessageHeader *hdr; | ||
272 | struct GNUNET_TIME_Relative delay; | ||
273 | |||
274 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
275 | "Sending TYPEMAP to %s\n", | ||
276 | GNUNET_i2s (session->peer)); | ||
277 | session->typemap_delay = GNUNET_TIME_STD_BACKOFF (session->typemap_delay); | ||
278 | delay = session->typemap_delay; | ||
279 | /* randomize a bit to avoid spont. sync */ | ||
280 | delay.rel_value_us += | ||
281 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000 * 1000); | ||
282 | session->typemap_task = | ||
283 | GNUNET_SCHEDULER_add_delayed (delay, &transmit_typemap_task, session); | ||
284 | GNUNET_STATISTICS_update (GSC_stats, | ||
285 | gettext_noop ("# type map refreshes sent"), | ||
286 | 1, | ||
287 | GNUNET_NO); | ||
288 | hdr = GSC_TYPEMAP_compute_type_map_message (); | ||
289 | GSC_KX_encrypt_and_transmit (session->kx, hdr, ntohs (hdr->size)); | ||
290 | GNUNET_free (hdr); | ||
291 | } | ||
292 | |||
293 | |||
294 | /** | ||
295 | * Restart the typemap task for the given session. | ||
296 | * | ||
297 | * @param session session to restart typemap transmission for | ||
298 | */ | ||
299 | static void | ||
300 | start_typemap_task (struct Session *session) | ||
301 | { | ||
302 | if (NULL != session->typemap_task) | ||
303 | GNUNET_SCHEDULER_cancel (session->typemap_task); | ||
304 | session->typemap_delay = GNUNET_TIME_UNIT_SECONDS; | ||
305 | session->typemap_task = GNUNET_SCHEDULER_add_delayed (session->typemap_delay, | ||
306 | &transmit_typemap_task, | ||
307 | session); | ||
308 | } | ||
309 | |||
310 | |||
311 | /** | ||
312 | * Create a session, a key exchange was just completed. | ||
313 | * | ||
314 | * @param peer peer that is now connected | ||
315 | * @param kx key exchange that completed | ||
316 | */ | ||
317 | void | ||
318 | GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer, | ||
319 | struct GSC_KeyExchangeInfo *kx) | ||
320 | { | ||
321 | struct Session *session; | ||
322 | |||
323 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
324 | "Creating session for peer `%s'\n", | ||
325 | GNUNET_i2s (peer)); | ||
326 | session = GNUNET_new (struct Session); | ||
327 | session->tmap = GSC_TYPEMAP_create (); | ||
328 | session->peer = peer; | ||
329 | session->kx = kx; | ||
330 | GNUNET_assert (GNUNET_OK == | ||
331 | GNUNET_CONTAINER_multipeermap_put ( | ||
332 | sessions, | ||
333 | session->peer, | ||
334 | session, | ||
335 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
336 | GNUNET_STATISTICS_set (GSC_stats, | ||
337 | gettext_noop ("# peers connected"), | ||
338 | GNUNET_CONTAINER_multipeermap_size (sessions), | ||
339 | GNUNET_NO); | ||
340 | GSC_CLIENTS_notify_clients_about_neighbour (peer, NULL, session->tmap); | ||
341 | start_typemap_task (session); | ||
342 | } | ||
343 | |||
344 | |||
345 | /** | ||
346 | * The other peer has indicated that it 'lost' the session | ||
347 | * (KX down), reinitialize the session on our end, in particular | ||
348 | * this means to restart the typemap transmission. | ||
349 | * | ||
350 | * @param peer peer that is now connected | ||
351 | */ | ||
352 | void | ||
353 | GSC_SESSIONS_reinit (const struct GNUNET_PeerIdentity *peer) | ||
354 | { | ||
355 | struct Session *session; | ||
356 | |||
357 | session = find_session (peer); | ||
358 | if (NULL == session) | ||
359 | { | ||
360 | /* KX/session is new for both sides; thus no need to restart what | ||
361 | has not yet begun */ | ||
362 | return; | ||
363 | } | ||
364 | start_typemap_task (session); | ||
365 | } | ||
366 | |||
367 | |||
368 | /** | ||
369 | * The other peer has confirmed receiving our type map, | ||
370 | * check if it is current and if so, stop retransmitting it. | ||
371 | * | ||
372 | * @param peer peer that confirmed the type map | ||
373 | * @param msg confirmation message we received | ||
374 | */ | ||
375 | void | ||
376 | GSC_SESSIONS_confirm_typemap (const struct GNUNET_PeerIdentity *peer, | ||
377 | const struct GNUNET_MessageHeader *msg) | ||
378 | { | ||
379 | const struct TypeMapConfirmationMessage *cmsg; | ||
380 | struct Session *session; | ||
381 | |||
382 | session = find_session (peer); | ||
383 | if (NULL == session) | ||
384 | { | ||
385 | GNUNET_break (0); | ||
386 | return; | ||
387 | } | ||
388 | if (ntohs (msg->size) != sizeof(struct TypeMapConfirmationMessage)) | ||
389 | { | ||
390 | GNUNET_break_op (0); | ||
391 | return; | ||
392 | } | ||
393 | cmsg = (const struct TypeMapConfirmationMessage *) msg; | ||
394 | if (GNUNET_YES != GSC_TYPEMAP_check_hash (&cmsg->tm_hash)) | ||
395 | { | ||
396 | /* our typemap has changed in the meantime, do not | ||
397 | accept confirmation */ | ||
398 | GNUNET_STATISTICS_update (GSC_stats, | ||
399 | gettext_noop ( | ||
400 | "# outdated typemap confirmations received"), | ||
401 | 1, | ||
402 | GNUNET_NO); | ||
403 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
404 | "Got outdated typemap confirmated from peer `%s'\n", | ||
405 | GNUNET_i2s (session->peer)); | ||
406 | return; | ||
407 | } | ||
408 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
409 | "Got typemap confirmation from peer `%s'\n", | ||
410 | GNUNET_i2s (session->peer)); | ||
411 | if (NULL != session->typemap_task) | ||
412 | { | ||
413 | GNUNET_SCHEDULER_cancel (session->typemap_task); | ||
414 | session->typemap_task = NULL; | ||
415 | } | ||
416 | GNUNET_STATISTICS_update (GSC_stats, | ||
417 | gettext_noop ( | ||
418 | "# valid typemap confirmations received"), | ||
419 | 1, | ||
420 | GNUNET_NO); | ||
421 | } | ||
422 | |||
423 | |||
424 | /** | ||
425 | * Notify the given client about the session (client is new). | ||
426 | * | ||
427 | * @param cls the `struct GSC_Client` | ||
428 | * @param key peer identity | ||
429 | * @param value the `struct Session` | ||
430 | * @return #GNUNET_OK (continue to iterate) | ||
431 | */ | ||
432 | static int | ||
433 | notify_client_about_session (void *cls, | ||
434 | const struct GNUNET_PeerIdentity *key, | ||
435 | void *value) | ||
436 | { | ||
437 | struct GSC_Client *client = cls; | ||
438 | struct Session *session = value; | ||
439 | |||
440 | GSC_CLIENTS_notify_client_about_neighbour (client, | ||
441 | session->peer, | ||
442 | NULL, /* old TMAP: none */ | ||
443 | session->tmap); | ||
444 | return GNUNET_OK; | ||
445 | } | ||
446 | |||
447 | |||
448 | /** | ||
449 | * We have a new client, notify it about all current sessions. | ||
450 | * | ||
451 | * @param client the new client | ||
452 | */ | ||
453 | void | ||
454 | GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client) | ||
455 | { | ||
456 | /* notify new client about existing sessions */ | ||
457 | GNUNET_CONTAINER_multipeermap_iterate (sessions, | ||
458 | ¬ify_client_about_session, | ||
459 | client); | ||
460 | } | ||
461 | |||
462 | |||
463 | /** | ||
464 | * Try to perform a transmission on the given session. Will solicit | ||
465 | * additional messages if the 'sme' queue is not full enough. | ||
466 | * | ||
467 | * @param session session to transmit messages from | ||
468 | */ | ||
469 | static void | ||
470 | try_transmission (struct Session *session); | ||
471 | |||
472 | |||
473 | /** | ||
474 | * Queue a request from a client for transmission to a particular peer. | ||
475 | * | ||
476 | * @param car request to queue; this handle is then shared between | ||
477 | * the caller (CLIENTS subsystem) and SESSIONS and must not | ||
478 | * be released by either until either #GSC_SESSIONS_dequeue(), | ||
479 | * #GSC_SESSIONS_transmit() or #GSC_CLIENTS_failed() | ||
480 | * have been invoked on it | ||
481 | */ | ||
482 | void | ||
483 | GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car) | ||
484 | { | ||
485 | struct Session *session; | ||
486 | |||
487 | session = find_session (&car->target); | ||
488 | if (NULL == session) | ||
489 | { | ||
490 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
491 | "Dropped client request for transmission (am disconnected)\n"); | ||
492 | GNUNET_break (0); /* should have been rejected earlier */ | ||
493 | GSC_CLIENTS_reject_request (car, GNUNET_NO); | ||
494 | return; | ||
495 | } | ||
496 | if (car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE) | ||
497 | { | ||
498 | GNUNET_break (0); | ||
499 | GSC_CLIENTS_reject_request (car, GNUNET_YES); | ||
500 | return; | ||
501 | } | ||
502 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
503 | "Received client transmission request. queueing\n"); | ||
504 | GNUNET_CONTAINER_DLL_insert_tail (session->active_client_request_head, | ||
505 | session->active_client_request_tail, | ||
506 | car); | ||
507 | try_transmission (session); | ||
508 | } | ||
509 | |||
510 | |||
511 | /** | ||
512 | * Dequeue a request from a client from transmission to a particular peer. | ||
513 | * | ||
514 | * @param car request to dequeue; this handle will then be 'owned' by | ||
515 | * the caller (CLIENTS sysbsystem) | ||
516 | */ | ||
517 | void | ||
518 | GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car) | ||
519 | { | ||
520 | struct Session *session; | ||
521 | |||
522 | if (0 == memcmp (&car->target, | ||
523 | &GSC_my_identity, | ||
524 | sizeof(struct GNUNET_PeerIdentity))) | ||
525 | return; | ||
526 | session = find_session (&car->target); | ||
527 | GNUNET_assert (NULL != session); | ||
528 | GNUNET_CONTAINER_DLL_remove (session->active_client_request_head, | ||
529 | session->active_client_request_tail, | ||
530 | car); | ||
531 | /* dequeueing of 'high' priority messages may unblock | ||
532 | transmission for lower-priority messages, so we also | ||
533 | need to try in this case. */ | ||
534 | try_transmission (session); | ||
535 | } | ||
536 | |||
537 | |||
538 | /** | ||
539 | * Solicit messages for transmission, starting with those of the highest | ||
540 | * priority. | ||
541 | * | ||
542 | * @param session session to solict messages for | ||
543 | * @param msize how many bytes do we have already | ||
544 | */ | ||
545 | static void | ||
546 | solicit_messages (struct Session *session, size_t msize) | ||
547 | { | ||
548 | struct GSC_ClientActiveRequest *car; | ||
549 | struct GSC_ClientActiveRequest *nxt; | ||
550 | size_t so_size; | ||
551 | enum GNUNET_MQ_PriorityPreferences pmax; | ||
552 | |||
553 | so_size = msize; | ||
554 | pmax = GNUNET_MQ_PRIO_BACKGROUND; | ||
555 | for (car = session->active_client_request_head; NULL != car; car = car->next) | ||
556 | { | ||
557 | if (GNUNET_YES == car->was_solicited) | ||
558 | continue; | ||
559 | pmax = GNUNET_MAX (pmax, car->priority & GNUNET_MQ_PRIORITY_MASK); | ||
560 | } | ||
561 | nxt = session->active_client_request_head; | ||
562 | while (NULL != (car = nxt)) | ||
563 | { | ||
564 | nxt = car->next; | ||
565 | if (car->priority < pmax) | ||
566 | continue; | ||
567 | if (so_size + car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE) | ||
568 | break; | ||
569 | so_size += car->msize; | ||
570 | if (GNUNET_YES == car->was_solicited) | ||
571 | continue; | ||
572 | car->was_solicited = GNUNET_YES; | ||
573 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
574 | "Soliciting message with priority %u\n", | ||
575 | car->priority); | ||
576 | GSC_CLIENTS_solicit_request (car); | ||
577 | /* The above call may *dequeue* requests and thereby | ||
578 | clobber 'nxt'. Hence we need to restart from the | ||
579 | head of the list. */ | ||
580 | nxt = session->active_client_request_head; | ||
581 | so_size = msize; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | |||
586 | /** | ||
587 | * Some messages were delayed (corked), but the timeout has now expired. | ||
588 | * Send them now. | ||
589 | * | ||
590 | * @param cls `struct Session` with the messages to transmit now | ||
591 | */ | ||
592 | static void | ||
593 | pop_cork_task (void *cls) | ||
594 | { | ||
595 | struct Session *session = cls; | ||
596 | |||
597 | session->cork_task = NULL; | ||
598 | try_transmission (session); | ||
599 | } | ||
600 | |||
601 | |||
602 | /** | ||
603 | * Try to perform a transmission on the given session. Will solicit | ||
604 | * additional messages if the 'sme' queue is not full enough or has | ||
605 | * only low-priority messages. | ||
606 | * | ||
607 | * @param session session to transmit messages from | ||
608 | */ | ||
609 | static void | ||
610 | try_transmission (struct Session *session) | ||
611 | { | ||
612 | struct SessionMessageEntry *pos; | ||
613 | size_t msize; | ||
614 | struct GNUNET_TIME_Absolute now; | ||
615 | struct GNUNET_TIME_Absolute min_deadline; | ||
616 | enum GNUNET_MQ_PriorityPreferences maxp; | ||
617 | enum GNUNET_MQ_PriorityPreferences maxpc; | ||
618 | struct GSC_ClientActiveRequest *car; | ||
619 | int excess; | ||
620 | |||
621 | msize = 0; | ||
622 | min_deadline = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
623 | /* if the peer has excess bandwidth, background traffic is allowed, | ||
624 | otherwise not */ | ||
625 | if (MAX_ENCRYPTED_MESSAGE_QUEUE_SIZE <= | ||
626 | GSC_NEIGHBOURS_get_queue_length (session->kx)) | ||
627 | { | ||
628 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
629 | "Transmission queue already very long, waiting...\n"); | ||
630 | return; /* queue already too long */ | ||
631 | } | ||
632 | excess = GSC_NEIGHBOURS_check_excess_bandwidth (session->kx); | ||
633 | if (GNUNET_YES == excess) | ||
634 | maxp = GNUNET_MQ_PRIO_BACKGROUND; | ||
635 | else | ||
636 | maxp = GNUNET_MQ_PRIO_BEST_EFFORT; | ||
637 | /* determine highest priority of 'ready' messages we already solicited from clients */ | ||
638 | pos = session->sme_head; | ||
639 | while ((NULL != pos) && | ||
640 | (msize + pos->size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)) | ||
641 | { | ||
642 | GNUNET_assert (pos->size < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE); | ||
643 | msize += pos->size; | ||
644 | maxp = GNUNET_MAX (maxp, pos->priority & GNUNET_MQ_PRIORITY_MASK); | ||
645 | min_deadline = GNUNET_TIME_absolute_min (min_deadline, pos->deadline); | ||
646 | pos = pos->next; | ||
647 | } | ||
648 | GNUNET_log ( | ||
649 | GNUNET_ERROR_TYPE_DEBUG, | ||
650 | "Calculating transmission set with %u priority (%s) and %s earliest deadline\n", | ||
651 | maxp, | ||
652 | (GNUNET_YES == excess) ? "excess bandwidth" : "limited bandwidth", | ||
653 | GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining ( | ||
654 | min_deadline), | ||
655 | GNUNET_YES)); | ||
656 | |||
657 | if (maxp < GNUNET_MQ_PRIO_CRITICAL_CONTROL) | ||
658 | { | ||
659 | /* if highest already solicited priority from clients is not critical, | ||
660 | check if there are higher-priority messages to be solicited from clients */ | ||
661 | if (GNUNET_YES == excess) | ||
662 | maxpc = GNUNET_MQ_PRIO_BACKGROUND; | ||
663 | else | ||
664 | maxpc = GNUNET_MQ_PRIO_BEST_EFFORT; | ||
665 | for (car = session->active_client_request_head; NULL != car; | ||
666 | car = car->next) | ||
667 | { | ||
668 | if (GNUNET_YES == car->was_solicited) | ||
669 | continue; | ||
670 | maxpc = GNUNET_MAX (maxpc, car->priority & GNUNET_MQ_PRIORITY_MASK); | ||
671 | } | ||
672 | if (maxpc > maxp) | ||
673 | { | ||
674 | /* we have messages waiting for solicitation that have a higher | ||
675 | priority than those that we already accepted; solicit the | ||
676 | high-priority messages first */ | ||
677 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
678 | "Soliciting messages based on priority (%u > %u)\n", | ||
679 | maxpc, | ||
680 | maxp); | ||
681 | solicit_messages (session, 0); | ||
682 | return; | ||
683 | } | ||
684 | } | ||
685 | else | ||
686 | { | ||
687 | /* never solicit more, we have critical messages to process */ | ||
688 | excess = GNUNET_NO; | ||
689 | maxpc = GNUNET_MQ_PRIO_BACKGROUND; | ||
690 | } | ||
691 | now = GNUNET_TIME_absolute_get (); | ||
692 | if (((GNUNET_YES == excess) || (maxpc >= GNUNET_MQ_PRIO_BEST_EFFORT)) && | ||
693 | ((0 == msize) || | ||
694 | ((msize < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE / 2) && | ||
695 | (min_deadline.abs_value_us > now.abs_value_us)))) | ||
696 | { | ||
697 | /* not enough ready yet (tiny message & cork possible), or no messages at all, | ||
698 | and either excess bandwidth or best-effort or higher message waiting at | ||
699 | client; in this case, we try to solicit more */ | ||
700 | GNUNET_log ( | ||
701 | GNUNET_ERROR_TYPE_DEBUG, | ||
702 | "Soliciting messages (excess %d, maxpc %d, message size %u, deadline %s)\n", | ||
703 | excess, | ||
704 | maxpc, | ||
705 | (unsigned int) msize, | ||
706 | GNUNET_STRINGS_relative_time_to_string ( | ||
707 | GNUNET_TIME_absolute_get_remaining ( | ||
708 | min_deadline), | ||
709 | GNUNET_YES)); | ||
710 | solicit_messages (session, msize); | ||
711 | if (msize > 0) | ||
712 | { | ||
713 | /* if there is data to send, just not yet, make sure we do transmit | ||
714 | * it once the deadline is reached */ | ||
715 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
716 | "Corking until %s\n", | ||
717 | GNUNET_STRINGS_relative_time_to_string ( | ||
718 | GNUNET_TIME_absolute_get_remaining (min_deadline), | ||
719 | GNUNET_YES)); | ||
720 | if (NULL != session->cork_task) | ||
721 | GNUNET_SCHEDULER_cancel (session->cork_task); | ||
722 | session->cork_task = | ||
723 | GNUNET_SCHEDULER_add_at (min_deadline, &pop_cork_task, session); | ||
724 | } | ||
725 | else | ||
726 | { | ||
727 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
728 | "Queue empty, waiting for solicitations\n"); | ||
729 | } | ||
730 | return; | ||
731 | } | ||
732 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
733 | "Building combined plaintext buffer to transmit message!\n"); | ||
734 | /* create plaintext buffer of all messages (that fit), encrypt and | ||
735 | transmit */ | ||
736 | { | ||
737 | static unsigned long long total_bytes; | ||
738 | static unsigned int total_msgs; | ||
739 | char pbuf[msize]; /* plaintext */ | ||
740 | size_t used; | ||
741 | |||
742 | used = 0; | ||
743 | while ((NULL != (pos = session->sme_head)) && (used + pos->size <= msize)) | ||
744 | { | ||
745 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
746 | "Adding message of type %d (%d/%d) to payload for %s\n", | ||
747 | ntohs (((const struct GNUNET_MessageHeader *) &pos[1])->type), | ||
748 | pos->is_typemap, | ||
749 | pos->is_typemap_confirm, | ||
750 | GNUNET_i2s (session->peer)); | ||
751 | GNUNET_memcpy (&pbuf[used], &pos[1], pos->size); | ||
752 | used += pos->size; | ||
753 | GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, pos); | ||
754 | GNUNET_free (pos); | ||
755 | } | ||
756 | /* compute average payload size */ | ||
757 | total_bytes += used; | ||
758 | total_msgs++; | ||
759 | if (0 == total_msgs) | ||
760 | { | ||
761 | /* 2^32 messages, wrap around... */ | ||
762 | total_msgs = 1; | ||
763 | total_bytes = used; | ||
764 | } | ||
765 | GNUNET_STATISTICS_set (GSC_stats, | ||
766 | "# avg payload per encrypted message", | ||
767 | total_bytes / total_msgs, | ||
768 | GNUNET_NO); | ||
769 | /* now actually transmit... */ | ||
770 | GSC_KX_encrypt_and_transmit (session->kx, pbuf, used); | ||
771 | } | ||
772 | } | ||
773 | |||
774 | |||
775 | /** | ||
776 | * Send an updated typemap message to the neighbour now, | ||
777 | * and restart typemap transmissions. | ||
778 | * | ||
779 | * @param cls the message | ||
780 | * @param key neighbour's identity | ||
781 | * @param value `struct Neighbour` of the target | ||
782 | * @return always #GNUNET_OK | ||
783 | */ | ||
784 | static int | ||
785 | do_restart_typemap_message (void *cls, | ||
786 | const struct GNUNET_PeerIdentity *key, | ||
787 | void *value) | ||
788 | { | ||
789 | const struct GNUNET_MessageHeader *hdr = cls; | ||
790 | struct Session *session = value; | ||
791 | struct SessionMessageEntry *sme; | ||
792 | uint16_t size; | ||
793 | |||
794 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
795 | "Restarting sending TYPEMAP to %s\n", | ||
796 | GNUNET_i2s (session->peer)); | ||
797 | size = ntohs (hdr->size); | ||
798 | for (sme = session->sme_head; NULL != sme; sme = sme->next) | ||
799 | { | ||
800 | if (GNUNET_YES == sme->is_typemap) | ||
801 | { | ||
802 | GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme); | ||
803 | GNUNET_free (sme); | ||
804 | break; | ||
805 | } | ||
806 | } | ||
807 | sme = GNUNET_malloc (sizeof(struct SessionMessageEntry) + size); | ||
808 | sme->is_typemap = GNUNET_YES; | ||
809 | GNUNET_memcpy (&sme[1], hdr, size); | ||
810 | sme->size = size; | ||
811 | sme->priority = GNUNET_MQ_PRIO_CRITICAL_CONTROL; | ||
812 | GNUNET_CONTAINER_DLL_insert (session->sme_head, session->sme_tail, sme); | ||
813 | try_transmission (session); | ||
814 | start_typemap_task (session); | ||
815 | return GNUNET_OK; | ||
816 | } | ||
817 | |||
818 | |||
819 | /** | ||
820 | * Broadcast an updated typemap message to all neighbours. | ||
821 | * Restarts the retransmissions until the typemaps are confirmed. | ||
822 | * | ||
823 | * @param msg message to transmit | ||
824 | */ | ||
825 | void | ||
826 | GSC_SESSIONS_broadcast_typemap (const struct GNUNET_MessageHeader *msg) | ||
827 | { | ||
828 | if (NULL == sessions) | ||
829 | return; | ||
830 | GNUNET_CONTAINER_multipeermap_iterate (sessions, | ||
831 | &do_restart_typemap_message, | ||
832 | (void *) msg); | ||
833 | } | ||
834 | |||
835 | |||
836 | /** | ||
837 | * Traffic is being solicited for the given peer. This means that the | ||
838 | * message queue on the transport-level (NEIGHBOURS subsystem) is now | ||
839 | * empty and it is now OK to transmit another (non-control) message. | ||
840 | * | ||
841 | * @param pid identity of peer ready to receive data | ||
842 | */ | ||
843 | void | ||
844 | GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid) | ||
845 | { | ||
846 | struct Session *session; | ||
847 | |||
848 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
849 | "Transport solicits for %s\n", | ||
850 | GNUNET_i2s (pid)); | ||
851 | session = find_session (pid); | ||
852 | if (NULL == session) | ||
853 | return; | ||
854 | try_transmission (session); | ||
855 | } | ||
856 | |||
857 | |||
858 | void | ||
859 | GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car, | ||
860 | const struct GNUNET_MessageHeader *msg, | ||
861 | enum GNUNET_MQ_PriorityPreferences priority) | ||
862 | { | ||
863 | struct Session *session; | ||
864 | struct SessionMessageEntry *sme; | ||
865 | struct SessionMessageEntry *pos; | ||
866 | size_t msize; | ||
867 | |||
868 | session = find_session (&car->target); | ||
869 | if (NULL == session) | ||
870 | return; | ||
871 | msize = ntohs (msg->size); | ||
872 | sme = GNUNET_malloc (sizeof(struct SessionMessageEntry) + msize); | ||
873 | GNUNET_memcpy (&sme[1], msg, msize); | ||
874 | sme->size = msize; | ||
875 | sme->priority = priority; | ||
876 | if (0 != (GNUNET_MQ_PREF_CORK_ALLOWED & priority)) | ||
877 | { | ||
878 | sme->deadline = | ||
879 | GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_MAX_CORK_DELAY); | ||
880 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
881 | "Message corked, delaying transmission\n"); | ||
882 | } | ||
883 | pos = session->sme_head; | ||
884 | while ((NULL != pos) && (pos->priority >= sme->priority)) | ||
885 | pos = pos->next; | ||
886 | if (NULL == pos) | ||
887 | GNUNET_CONTAINER_DLL_insert_tail (session->sme_head, | ||
888 | session->sme_tail, | ||
889 | sme); | ||
890 | else | ||
891 | GNUNET_CONTAINER_DLL_insert_after (session->sme_head, | ||
892 | session->sme_tail, | ||
893 | pos->prev, | ||
894 | sme); | ||
895 | try_transmission (session); | ||
896 | } | ||
897 | |||
898 | |||
899 | void | ||
900 | GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer, | ||
901 | const struct GNUNET_MessageHeader *msg) | ||
902 | { | ||
903 | struct Session *session; | ||
904 | struct GSC_TypeMap *nmap; | ||
905 | struct SessionMessageEntry *sme; | ||
906 | struct TypeMapConfirmationMessage *tmc; | ||
907 | |||
908 | nmap = GSC_TYPEMAP_get_from_message (msg); | ||
909 | if (NULL == nmap) | ||
910 | { | ||
911 | GNUNET_break_op (0); | ||
912 | return; /* malformed */ | ||
913 | } | ||
914 | session = find_session (peer); | ||
915 | if (NULL == session) | ||
916 | { | ||
917 | GSC_TYPEMAP_destroy (nmap); | ||
918 | GNUNET_break (0); | ||
919 | return; | ||
920 | } | ||
921 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
922 | "Received TYPEMAP from %s\n", | ||
923 | GNUNET_i2s (session->peer)); | ||
924 | for (sme = session->sme_head; NULL != sme; sme = sme->next) | ||
925 | { | ||
926 | if (GNUNET_YES == sme->is_typemap_confirm) | ||
927 | { | ||
928 | GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme); | ||
929 | GNUNET_free (sme); | ||
930 | break; | ||
931 | } | ||
932 | } | ||
933 | sme = GNUNET_malloc (sizeof(struct SessionMessageEntry) | ||
934 | + sizeof(struct TypeMapConfirmationMessage)); | ||
935 | sme->deadline = GNUNET_TIME_absolute_get (); | ||
936 | sme->size = sizeof(struct TypeMapConfirmationMessage); | ||
937 | sme->priority = GNUNET_MQ_PRIO_CRITICAL_CONTROL; | ||
938 | sme->is_typemap_confirm = GNUNET_YES; | ||
939 | tmc = (struct TypeMapConfirmationMessage *) &sme[1]; | ||
940 | tmc->header.size = htons (sizeof(struct TypeMapConfirmationMessage)); | ||
941 | tmc->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP); | ||
942 | tmc->reserved = htonl (0); | ||
943 | GSC_TYPEMAP_hash (nmap, &tmc->tm_hash); | ||
944 | GNUNET_CONTAINER_DLL_insert (session->sme_head, session->sme_tail, sme); | ||
945 | try_transmission (session); | ||
946 | GSC_CLIENTS_notify_clients_about_neighbour (peer, session->tmap, nmap); | ||
947 | GSC_TYPEMAP_destroy (session->tmap); | ||
948 | session->tmap = nmap; | ||
949 | } | ||
950 | |||
951 | |||
952 | /** | ||
953 | * The given peer send a message of the specified type. Make sure the | ||
954 | * respective bit is set in its type-map and that clients are notified | ||
955 | * about the session. | ||
956 | * | ||
957 | * @param peer peer this is about | ||
958 | * @param type type of the message | ||
959 | */ | ||
960 | void | ||
961 | GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer, | ||
962 | uint16_t type) | ||
963 | { | ||
964 | struct Session *session; | ||
965 | struct GSC_TypeMap *nmap; | ||
966 | |||
967 | if (0 == memcmp (peer, &GSC_my_identity, sizeof(struct GNUNET_PeerIdentity))) | ||
968 | return; | ||
969 | session = find_session (peer); | ||
970 | GNUNET_assert (NULL != session); | ||
971 | if (GNUNET_YES == GSC_TYPEMAP_test_match (session->tmap, &type, 1)) | ||
972 | return; /* already in it */ | ||
973 | nmap = GSC_TYPEMAP_extend (session->tmap, &type, 1); | ||
974 | GSC_CLIENTS_notify_clients_about_neighbour (peer, session->tmap, nmap); | ||
975 | GSC_TYPEMAP_destroy (session->tmap); | ||
976 | session->tmap = nmap; | ||
977 | } | ||
978 | |||
979 | |||
980 | /** | ||
981 | * Initialize sessions subsystem. | ||
982 | */ | ||
983 | void | ||
984 | GSC_SESSIONS_init () | ||
985 | { | ||
986 | sessions = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES); | ||
987 | } | ||
988 | |||
989 | |||
990 | /** | ||
991 | * Helper function for #GSC_SESSIONS_done() to free all | ||
992 | * active sessions. | ||
993 | * | ||
994 | * @param cls NULL | ||
995 | * @param key identity of the connected peer | ||
996 | * @param value the `struct Session` for the peer | ||
997 | * @return #GNUNET_OK (continue to iterate) | ||
998 | */ | ||
999 | static int | ||
1000 | free_session_helper (void *cls, | ||
1001 | const struct GNUNET_PeerIdentity *key, | ||
1002 | void *value) | ||
1003 | { | ||
1004 | /* struct Session *session = value; */ | ||
1005 | |||
1006 | GSC_SESSIONS_end (key); | ||
1007 | return GNUNET_OK; | ||
1008 | } | ||
1009 | |||
1010 | |||
1011 | /** | ||
1012 | * Shutdown sessions subsystem. | ||
1013 | */ | ||
1014 | void | ||
1015 | GSC_SESSIONS_done () | ||
1016 | { | ||
1017 | if (NULL != sessions) | ||
1018 | { | ||
1019 | GNUNET_CONTAINER_multipeermap_iterate (sessions, | ||
1020 | &free_session_helper, | ||
1021 | NULL); | ||
1022 | GNUNET_CONTAINER_multipeermap_destroy (sessions); | ||
1023 | sessions = NULL; | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | |||
1028 | /* end of gnunet-service-core_sessions.c */ | ||
diff --git a/src/service/core/gnunet-service-core_sessions.h b/src/service/core/gnunet-service-core_sessions.h new file mode 100644 index 000000000..fda2cc32c --- /dev/null +++ b/src/service/core/gnunet-service-core_sessions.h | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009-2014 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core_neighbours.h | ||
23 | * @brief code for managing of 'encrypted' sessions (key exchange done) | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef GNUNET_SERVICE_CORE_SESSIONS_H | ||
27 | #define GNUNET_SERVICE_CORE_SESSIONS_H | ||
28 | |||
29 | #include "gnunet-service-core.h" | ||
30 | #include "gnunet-service-core_kx.h" | ||
31 | |||
32 | |||
33 | /** | ||
34 | * Create a session, a key exchange was just completed. | ||
35 | * | ||
36 | * @param peer peer that is now connected | ||
37 | * @param kx key exchange that completed | ||
38 | */ | ||
39 | void | ||
40 | GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer, | ||
41 | struct GSC_KeyExchangeInfo *kx); | ||
42 | |||
43 | |||
44 | /** | ||
45 | * The other peer has indicated that it 'lost' the session | ||
46 | * (KX down), reinitialize the session on our end, in particular | ||
47 | * this means to restart the typemap transmission. | ||
48 | * | ||
49 | * @param peer peer that is now connected | ||
50 | */ | ||
51 | void | ||
52 | GSC_SESSIONS_reinit (const struct GNUNET_PeerIdentity *peer); | ||
53 | |||
54 | |||
55 | /** | ||
56 | * The other peer has confirmed receiving our type map, | ||
57 | * check if it is current and if so, stop retransmitting it. | ||
58 | * | ||
59 | * @param peer peer that confirmed the type map | ||
60 | * @param msg confirmation message we received | ||
61 | */ | ||
62 | void | ||
63 | GSC_SESSIONS_confirm_typemap (const struct GNUNET_PeerIdentity *peer, | ||
64 | const struct GNUNET_MessageHeader *msg); | ||
65 | |||
66 | |||
67 | /** | ||
68 | * End the session with the given peer (we are no longer | ||
69 | * connected). | ||
70 | * | ||
71 | * @param pid identity of peer to kill session with | ||
72 | */ | ||
73 | void | ||
74 | GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid); | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Traffic is being solicited for the given peer. This means that the | ||
79 | * message queue on the transport-level (NEIGHBOURS subsystem) is now | ||
80 | * empty and it is now OK to transmit another (non-control) message. | ||
81 | * | ||
82 | * @param pid identity of peer ready to receive data | ||
83 | */ | ||
84 | void | ||
85 | GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid); | ||
86 | |||
87 | |||
88 | /** | ||
89 | * Queue a request from a client for transmission to a particular peer. | ||
90 | * | ||
91 | * @param car request to queue; this handle is then shared between | ||
92 | * the caller (CLIENTS subsystem) and SESSIONS and must not | ||
93 | * be released by either until either 'GNUNET_SESSIONS_dequeue', | ||
94 | * or 'GNUNET_CLIENTS_failed' | ||
95 | * have been invoked on it | ||
96 | */ | ||
97 | void | ||
98 | GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car); | ||
99 | |||
100 | |||
101 | /** | ||
102 | * Dequeue a request from a client from transmission to a particular peer. | ||
103 | * | ||
104 | * @param car request to dequeue; this handle will then be 'owned' by | ||
105 | * the caller (CLIENTS sysbsystem) | ||
106 | */ | ||
107 | void | ||
108 | GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car); | ||
109 | |||
110 | |||
111 | /** | ||
112 | * Transmit a message to a particular peer. | ||
113 | * | ||
114 | * @param car original request that was queued and then solicited, | ||
115 | * ownership does not change (dequeue will be called soon). | ||
116 | * @param msg message to transmit | ||
117 | * @param priority how important is this message | ||
118 | */ | ||
119 | void | ||
120 | GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car, | ||
121 | const struct GNUNET_MessageHeader *msg, | ||
122 | enum GNUNET_MQ_PriorityPreferences priority); | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Broadcast an updated typemap message to all neighbours. | ||
127 | * Restarts the retransmissions until the typemaps are confirmed. | ||
128 | * | ||
129 | * @param msg message to transmit | ||
130 | */ | ||
131 | void | ||
132 | GSC_SESSIONS_broadcast_typemap (const struct GNUNET_MessageHeader *msg); | ||
133 | |||
134 | |||
135 | /** | ||
136 | * We have a new client, notify it about all current sessions. | ||
137 | * | ||
138 | * @param client the new client | ||
139 | */ | ||
140 | void | ||
141 | GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client); | ||
142 | |||
143 | |||
144 | /** | ||
145 | * We've received a typemap message from a peer, update ours. | ||
146 | * Notifies clients about the session. | ||
147 | * | ||
148 | * @param peer peer this is about | ||
149 | * @param msg typemap update message | ||
150 | */ | ||
151 | void | ||
152 | GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer, | ||
153 | const struct GNUNET_MessageHeader *msg); | ||
154 | |||
155 | |||
156 | /** | ||
157 | * The given peer send a message of the specified type. Make sure the | ||
158 | * respective bit is set in its type-map and that clients are notified | ||
159 | * about the session. | ||
160 | * | ||
161 | * @param peer peer this is about | ||
162 | * @param type type of the message | ||
163 | */ | ||
164 | void | ||
165 | GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer, | ||
166 | uint16_t type); | ||
167 | |||
168 | |||
169 | /** | ||
170 | * Initialize sessions subsystem. | ||
171 | */ | ||
172 | void | ||
173 | GSC_SESSIONS_init (void); | ||
174 | |||
175 | |||
176 | /** | ||
177 | * Shutdown sessions subsystem. | ||
178 | */ | ||
179 | void | ||
180 | GSC_SESSIONS_done (void); | ||
181 | |||
182 | |||
183 | #endif | ||
diff --git a/src/service/core/gnunet-service-core_typemap.c b/src/service/core/gnunet-service-core_typemap.c new file mode 100644 index 000000000..200a84b23 --- /dev/null +++ b/src/service/core/gnunet-service-core_typemap.c | |||
@@ -0,0 +1,370 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011-2014 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core_typemap.c | ||
23 | * @brief management of map that specifies which message types this peer supports | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet-service-core.h" | ||
29 | #include "gnunet-service-core_sessions.h" | ||
30 | #include "gnunet-service-core_typemap.h" | ||
31 | #include <zlib.h> | ||
32 | |||
33 | |||
34 | /** | ||
35 | * A type map describing which messages a given neighbour is able | ||
36 | * to process. | ||
37 | */ | ||
38 | struct GSC_TypeMap | ||
39 | { | ||
40 | uint32_t bits[(UINT16_MAX + 1) / 32]; | ||
41 | }; | ||
42 | |||
43 | /** | ||
44 | * Bitmap of message types this peer is able to handle. | ||
45 | */ | ||
46 | static struct GSC_TypeMap my_type_map; | ||
47 | |||
48 | /** | ||
49 | * Counters for message types this peer is able to handle. | ||
50 | */ | ||
51 | static uint8_t map_counters[UINT16_MAX + 1]; | ||
52 | |||
53 | /** | ||
54 | * Current hash of our (uncompressed) type map. | ||
55 | * Lazily computed when needed. | ||
56 | */ | ||
57 | static struct GNUNET_HashCode my_tm_hash; | ||
58 | |||
59 | /** | ||
60 | * Is #my_tm_hash() current with respect to our type map? | ||
61 | */ | ||
62 | static int hash_current; | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Our type map changed, recompute its hash. | ||
67 | */ | ||
68 | static void | ||
69 | rehash_typemap () | ||
70 | { | ||
71 | hash_current = GNUNET_NO; | ||
72 | } | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Hash the contents of a type map. | ||
77 | * | ||
78 | * @param tm map to hash | ||
79 | * @param hc where to store the hash code | ||
80 | */ | ||
81 | void | ||
82 | GSC_TYPEMAP_hash (const struct GSC_TypeMap *tm, struct GNUNET_HashCode *hc) | ||
83 | { | ||
84 | GNUNET_CRYPTO_hash (tm, sizeof(struct GSC_TypeMap), hc); | ||
85 | } | ||
86 | |||
87 | |||
88 | /** | ||
89 | * Check if the given hash matches our current type map. | ||
90 | * | ||
91 | * @param hc hash code to check if it matches our type map | ||
92 | * @return #GNUNET_YES if the hash matches, #GNUNET_NO if not | ||
93 | */ | ||
94 | int | ||
95 | GSC_TYPEMAP_check_hash (const struct GNUNET_HashCode *hc) | ||
96 | { | ||
97 | if (GNUNET_NO == hash_current) | ||
98 | { | ||
99 | GSC_TYPEMAP_hash (&my_type_map, &my_tm_hash); | ||
100 | hash_current = GNUNET_YES; | ||
101 | } | ||
102 | return (0 == memcmp (hc, &my_tm_hash, sizeof(struct GNUNET_HashCode))) | ||
103 | ? GNUNET_YES | ||
104 | : GNUNET_NO; | ||
105 | } | ||
106 | |||
107 | |||
108 | /** | ||
109 | * Compute a type map message for this peer. | ||
110 | * | ||
111 | * @return this peers current type map message. | ||
112 | */ | ||
113 | struct GNUNET_MessageHeader * | ||
114 | GSC_TYPEMAP_compute_type_map_message () | ||
115 | { | ||
116 | char *tmp; | ||
117 | uLongf dlen; | ||
118 | struct GNUNET_MessageHeader *hdr; | ||
119 | |||
120 | #ifdef compressBound | ||
121 | dlen = compressBound (sizeof(my_type_map)); | ||
122 | #else | ||
123 | dlen = sizeof(my_type_map) + (sizeof(my_type_map) / 100) + 20; | ||
124 | /* documentation says 100.1% oldSize + 12 bytes, but we | ||
125 | * should be able to overshoot by more to be safe */ | ||
126 | #endif | ||
127 | hdr = GNUNET_malloc (dlen + sizeof(struct GNUNET_MessageHeader)); | ||
128 | tmp = (char *) &hdr[1]; | ||
129 | if ((Z_OK != compress2 ((Bytef *) tmp, | ||
130 | &dlen, | ||
131 | (const Bytef *) &my_type_map, | ||
132 | sizeof(my_type_map), | ||
133 | 9)) || | ||
134 | (dlen >= sizeof(my_type_map))) | ||
135 | { | ||
136 | /* compression failed, use uncompressed map */ | ||
137 | dlen = sizeof(my_type_map); | ||
138 | GNUNET_memcpy (tmp, &my_type_map, sizeof(my_type_map)); | ||
139 | hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP); | ||
140 | } | ||
141 | else | ||
142 | { | ||
143 | /* compression worked, use compressed map */ | ||
144 | hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP); | ||
145 | } | ||
146 | hdr->size = htons ((uint16_t) dlen + sizeof(struct GNUNET_MessageHeader)); | ||
147 | return hdr; | ||
148 | } | ||
149 | |||
150 | |||
151 | /** | ||
152 | * Extract a type map from a TYPE_MAP message. | ||
153 | * | ||
154 | * @param msg a type map message | ||
155 | * @return NULL on error | ||
156 | */ | ||
157 | struct GSC_TypeMap * | ||
158 | GSC_TYPEMAP_get_from_message (const struct GNUNET_MessageHeader *msg) | ||
159 | { | ||
160 | struct GSC_TypeMap *ret; | ||
161 | uint16_t size; | ||
162 | uLongf dlen; | ||
163 | |||
164 | size = ntohs (msg->size); | ||
165 | switch (ntohs (msg->type)) | ||
166 | { | ||
167 | case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP: | ||
168 | GNUNET_STATISTICS_update (GSC_stats, | ||
169 | gettext_noop ("# type maps received"), | ||
170 | 1, | ||
171 | GNUNET_NO); | ||
172 | if (size != sizeof(struct GSC_TypeMap)) | ||
173 | { | ||
174 | GNUNET_break_op (0); | ||
175 | return NULL; | ||
176 | } | ||
177 | ret = GNUNET_new (struct GSC_TypeMap); | ||
178 | GNUNET_memcpy (ret, &msg[1], sizeof(struct GSC_TypeMap)); | ||
179 | return ret; | ||
180 | |||
181 | case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP: | ||
182 | GNUNET_STATISTICS_update (GSC_stats, | ||
183 | gettext_noop ("# type maps received"), | ||
184 | 1, | ||
185 | GNUNET_NO); | ||
186 | ret = GNUNET_new (struct GSC_TypeMap); | ||
187 | dlen = sizeof(struct GSC_TypeMap); | ||
188 | if ((Z_OK != uncompress ((Bytef *) ret, | ||
189 | &dlen, | ||
190 | (const Bytef *) &msg[1], | ||
191 | (uLong) size)) || | ||
192 | (dlen != sizeof(struct GSC_TypeMap))) | ||
193 | { | ||
194 | GNUNET_break_op (0); | ||
195 | GNUNET_free (ret); | ||
196 | return NULL; | ||
197 | } | ||
198 | return ret; | ||
199 | |||
200 | default: | ||
201 | GNUNET_break (0); | ||
202 | return NULL; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | |||
207 | /** | ||
208 | * Send my type map to all connected peers (it got changed). | ||
209 | */ | ||
210 | static void | ||
211 | broadcast_my_type_map () | ||
212 | { | ||
213 | struct GNUNET_MessageHeader *hdr; | ||
214 | |||
215 | hdr = GSC_TYPEMAP_compute_type_map_message (); | ||
216 | GNUNET_STATISTICS_update (GSC_stats, | ||
217 | gettext_noop ("# updates to my type map"), | ||
218 | 1, | ||
219 | GNUNET_NO); | ||
220 | GSC_SESSIONS_broadcast_typemap (hdr); | ||
221 | GNUNET_free (hdr); | ||
222 | } | ||
223 | |||
224 | |||
225 | /** | ||
226 | * Add a set of types to our type map. | ||
227 | * | ||
228 | * @param types array of message types supported by this peer | ||
229 | * @param tlen number of entries in @a types | ||
230 | */ | ||
231 | void | ||
232 | GSC_TYPEMAP_add (const uint16_t *types, unsigned int tlen) | ||
233 | { | ||
234 | unsigned int i; | ||
235 | int changed; | ||
236 | |||
237 | changed = GNUNET_NO; | ||
238 | for (i = 0; i < tlen; i++) | ||
239 | { | ||
240 | if (0 == map_counters[types[i]]++) | ||
241 | { | ||
242 | my_type_map.bits[types[i] / 32] |= (1 << (types[i] % 32)); | ||
243 | changed = GNUNET_YES; | ||
244 | } | ||
245 | } | ||
246 | if (GNUNET_YES == changed) | ||
247 | { | ||
248 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Typemap changed, broadcasting!\n"); | ||
249 | rehash_typemap (); | ||
250 | broadcast_my_type_map (); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | |||
255 | void | ||
256 | GSC_TYPEMAP_remove (const uint16_t *types, unsigned int tlen) | ||
257 | { | ||
258 | int changed; | ||
259 | |||
260 | changed = GNUNET_NO; | ||
261 | for (unsigned int i = 0; i < tlen; i++) | ||
262 | { | ||
263 | if (0 == --map_counters[types[i]]) | ||
264 | { | ||
265 | my_type_map.bits[types[i] / 32] &= ~(1 << (types[i] % 32)); | ||
266 | changed = GNUNET_YES; | ||
267 | } | ||
268 | } | ||
269 | if (GNUNET_YES == changed) | ||
270 | { | ||
271 | rehash_typemap (); | ||
272 | broadcast_my_type_map (); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | |||
277 | /** | ||
278 | * Test if any of the types from the types array is in the | ||
279 | * given type map. | ||
280 | * | ||
281 | * @param tmap map to test | ||
282 | * @param types array of types | ||
283 | * @param tcnt number of entries in @a types | ||
284 | * @return #GNUNET_YES if a type is in the map, #GNUNET_NO if not | ||
285 | */ | ||
286 | int | ||
287 | GSC_TYPEMAP_test_match (const struct GSC_TypeMap *tmap, | ||
288 | const uint16_t *types, | ||
289 | unsigned int tcnt) | ||
290 | { | ||
291 | if (NULL == tmap) | ||
292 | return GNUNET_NO; | ||
293 | if (0 == tcnt) | ||
294 | return GNUNET_YES; /* matches all */ | ||
295 | for (unsigned int i = 0; i < tcnt; i++) | ||
296 | if (0 != (tmap->bits[types[i] / 32] & (1 << (types[i] % 32)))) | ||
297 | return GNUNET_YES; | ||
298 | return GNUNET_NO; | ||
299 | } | ||
300 | |||
301 | |||
302 | /** | ||
303 | * Add additional types to a given typemap. | ||
304 | * | ||
305 | * @param tmap map to extend (not changed) | ||
306 | * @param types array of types to add | ||
307 | * @param tcnt number of entries in @a types | ||
308 | * @return updated type map (fresh copy) | ||
309 | */ | ||
310 | struct GSC_TypeMap * | ||
311 | GSC_TYPEMAP_extend (const struct GSC_TypeMap *tmap, | ||
312 | const uint16_t *types, | ||
313 | unsigned int tcnt) | ||
314 | { | ||
315 | struct GSC_TypeMap *ret; | ||
316 | |||
317 | ret = GNUNET_new (struct GSC_TypeMap); | ||
318 | if (NULL != tmap) | ||
319 | GNUNET_memcpy (ret, tmap, sizeof(struct GSC_TypeMap)); | ||
320 | for (unsigned int i = 0; i < tcnt; i++) | ||
321 | ret->bits[types[i] / 32] |= (1 << (types[i] % 32)); | ||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | |||
326 | /** | ||
327 | * Create an empty type map. | ||
328 | * | ||
329 | * @return an empty type map | ||
330 | */ | ||
331 | struct GSC_TypeMap * | ||
332 | GSC_TYPEMAP_create () | ||
333 | { | ||
334 | return GNUNET_new (struct GSC_TypeMap); | ||
335 | } | ||
336 | |||
337 | |||
338 | /** | ||
339 | * Free the given type map. | ||
340 | * | ||
341 | * @param tmap a type map | ||
342 | */ | ||
343 | void | ||
344 | GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap) | ||
345 | { | ||
346 | GNUNET_free (tmap); | ||
347 | } | ||
348 | |||
349 | |||
350 | /** | ||
351 | * Initialize typemap subsystem. | ||
352 | */ | ||
353 | void | ||
354 | GSC_TYPEMAP_init () | ||
355 | { | ||
356 | /* nothing to do */ | ||
357 | } | ||
358 | |||
359 | |||
360 | /** | ||
361 | * Shutdown typemap subsystem. | ||
362 | */ | ||
363 | void | ||
364 | GSC_TYPEMAP_done () | ||
365 | { | ||
366 | /* nothing to do */ | ||
367 | } | ||
368 | |||
369 | |||
370 | /* end of gnunet-service-core_typemap.c */ | ||
diff --git a/src/service/core/gnunet-service-core_typemap.h b/src/service/core/gnunet-service-core_typemap.h new file mode 100644 index 000000000..de41f4220 --- /dev/null +++ b/src/service/core/gnunet-service-core_typemap.h | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/gnunet-service-core_typemap.h | ||
23 | * @brief management of map that specifies which message types this peer supports | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef GNUNET_SERVICE_CORE_TYPEMAP_H | ||
27 | #define GNUNET_SERVICE_CORE_TYPEMAP_H | ||
28 | |||
29 | #include "gnunet_util_lib.h" | ||
30 | |||
31 | /** | ||
32 | * Map specifying which message types a peer supports. | ||
33 | */ | ||
34 | struct GSC_TypeMap; | ||
35 | |||
36 | |||
37 | /** | ||
38 | * Add a set of types to our type map. | ||
39 | * | ||
40 | * @param types array of message types supported by this peer | ||
41 | * @param tlen number of entries in @a types | ||
42 | */ | ||
43 | void | ||
44 | GSC_TYPEMAP_add (const uint16_t *types, | ||
45 | unsigned int tlen); | ||
46 | |||
47 | |||
48 | /** | ||
49 | * Remove a set of message types from our type map. | ||
50 | * | ||
51 | * @param types array of message types no longer supported by this peer | ||
52 | * @param tlen number of entries in @a types | ||
53 | */ | ||
54 | void | ||
55 | GSC_TYPEMAP_remove (const uint16_t *types, | ||
56 | unsigned int tlen); | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Compute a type map message for this peer. | ||
61 | * | ||
62 | * @return this peers current type map message. | ||
63 | */ | ||
64 | struct GNUNET_MessageHeader * | ||
65 | GSC_TYPEMAP_compute_type_map_message (void); | ||
66 | |||
67 | |||
68 | /** | ||
69 | * Check if the given hash matches our current type map. | ||
70 | * | ||
71 | * @param hc hash code to check if it matches our type map | ||
72 | * @return #GNUNET_YES if the hash matches, #GNUNET_NO if not | ||
73 | */ | ||
74 | int | ||
75 | GSC_TYPEMAP_check_hash (const struct GNUNET_HashCode *hc); | ||
76 | |||
77 | |||
78 | /** | ||
79 | * Hash the contents of a type map. | ||
80 | * | ||
81 | * @param tm map to hash | ||
82 | * @param hc where to store the hash code | ||
83 | */ | ||
84 | void | ||
85 | GSC_TYPEMAP_hash (const struct GSC_TypeMap *tm, | ||
86 | struct GNUNET_HashCode *hc); | ||
87 | |||
88 | |||
89 | /** | ||
90 | * Extract a type map from a | ||
91 | * #GNUNET_MESSAGE_TYPE_CORE_COMRESSED_TYPE_MAP or | ||
92 | * #GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP message. | ||
93 | * | ||
94 | * @param msg a type map message | ||
95 | * @return NULL on error | ||
96 | */ | ||
97 | struct GSC_TypeMap * | ||
98 | GSC_TYPEMAP_get_from_message (const struct GNUNET_MessageHeader *msg); | ||
99 | |||
100 | |||
101 | /** | ||
102 | * Test if any of the types from the types array is in the | ||
103 | * given type map. | ||
104 | * | ||
105 | * @param tmap map to test | ||
106 | * @param types array of types | ||
107 | * @param tcnt number of entries in @a types | ||
108 | * @return #GNUNET_YES if a type is in the map, #GNUNET_NO if not | ||
109 | */ | ||
110 | int | ||
111 | GSC_TYPEMAP_test_match (const struct GSC_TypeMap *tmap, | ||
112 | const uint16_t *types, | ||
113 | unsigned int tcnt); | ||
114 | |||
115 | |||
116 | /** | ||
117 | * Add additional types to a given typemap. | ||
118 | * | ||
119 | * @param tmap map to extend (not changed) | ||
120 | * @param types array of types to add | ||
121 | * @param tcnt number of entries in @a types | ||
122 | * @return updated type map (fresh copy) | ||
123 | */ | ||
124 | struct GSC_TypeMap * | ||
125 | GSC_TYPEMAP_extend (const struct GSC_TypeMap *tmap, | ||
126 | const uint16_t *types, | ||
127 | unsigned int tcnt); | ||
128 | |||
129 | |||
130 | /** | ||
131 | * Create an empty type map. | ||
132 | * | ||
133 | * @return an empty type map | ||
134 | */ | ||
135 | struct GSC_TypeMap * | ||
136 | GSC_TYPEMAP_create (void); | ||
137 | |||
138 | |||
139 | /** | ||
140 | * Free the given type map. | ||
141 | * | ||
142 | * @param tmap a type map | ||
143 | */ | ||
144 | void | ||
145 | GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap); | ||
146 | |||
147 | |||
148 | /** | ||
149 | * Initialize typemap subsystem. | ||
150 | */ | ||
151 | void | ||
152 | GSC_TYPEMAP_init (void); | ||
153 | |||
154 | |||
155 | /** | ||
156 | * Shutdown typemap subsystem. | ||
157 | */ | ||
158 | void | ||
159 | GSC_TYPEMAP_done (void); | ||
160 | |||
161 | #endif | ||
162 | /* end of gnunet-service-core_typemap.h */ | ||
diff --git a/src/service/core/meson.build b/src/service/core/meson.build new file mode 100644 index 000000000..09fdc8dab --- /dev/null +++ b/src/service/core/meson.build | |||
@@ -0,0 +1,113 @@ | |||
1 | libgnunetcore_src = ['core_api.c', | ||
2 | 'core_api_monitor_peers.c'] | ||
3 | |||
4 | gnunetservicecore_src = ['gnunet-service-core.c', | ||
5 | 'gnunet-service-core_kx.c', | ||
6 | 'gnunet-service-core_sessions.c', | ||
7 | 'gnunet-service-core_typemap.c'] | ||
8 | |||
9 | configure_file(input : 'core.conf.in', | ||
10 | output : 'core.conf', | ||
11 | configuration : cdata, | ||
12 | install: true, | ||
13 | install_dir: pkgcfgdir) | ||
14 | |||
15 | |||
16 | if get_option('monolith') | ||
17 | foreach p : libgnunetcore_src + gnunetservicecore_src | ||
18 | gnunet_src += 'core/' + p | ||
19 | endforeach | ||
20 | endif | ||
21 | |||
22 | libgnunetcore = library('gnunetcore', | ||
23 | libgnunetcore_src, | ||
24 | dependencies: libgnunetutil_dep, | ||
25 | include_directories: [incdir, configuration_inc], | ||
26 | install: true, | ||
27 | soversion: '0', | ||
28 | version: '0.0.1', | ||
29 | install_dir: get_option('libdir')) | ||
30 | libgnunetcore_dep = declare_dependency(link_with : libgnunetcore) | ||
31 | pkg.generate(libgnunetcore, url: 'https://www.gnunet.org', | ||
32 | description : 'Provides API for (encrypted) P2P communication') | ||
33 | |||
34 | libgnunetcoretesting = library('gnunetcoretesting', | ||
35 | ['core_api_cmd_connecting_peers.c'], | ||
36 | dependencies: [ | ||
37 | libgnunetutil_dep, | ||
38 | libgnunettransporttesting2_dep, | ||
39 | libgnunettesting_dep, | ||
40 | libgnunetarm_dep, | ||
41 | libgnunettransportapplication_dep, | ||
42 | libgnunettransportcore_dep, | ||
43 | ], | ||
44 | include_directories: [incdir, configuration_inc], | ||
45 | install: true, | ||
46 | soversion: '0', | ||
47 | version: '0.0.0', | ||
48 | install_dir: get_option('libdir')) | ||
49 | libgnunetcoretesting_dep = declare_dependency(link_with : libgnunetcoretesting) | ||
50 | |||
51 | shared_module('gnunet_test_core_plugin_cmd_just_run', | ||
52 | ['test_core_plugin_cmd_just_run.c'], | ||
53 | dependencies: [libgnunetutil_dep, | ||
54 | libgnunettesting_dep, | ||
55 | libgnunetcoretesting_dep, | ||
56 | libgnunettransporttesting2_dep, | ||
57 | libgnunettransportcore_dep, | ||
58 | libgnunettransportapplication_dep, | ||
59 | libgnunettesting_dep, | ||
60 | libgnunetpeerstore_dep, | ||
61 | libgnunetstatistics_dep, | ||
62 | libgnunethello_dep, | ||
63 | libgnunetarm_dep | ||
64 | ], | ||
65 | include_directories: [incdir, configuration_inc], | ||
66 | install: false) | ||
67 | |||
68 | executable ('gnunet-service-core', | ||
69 | gnunetservicecore_src, | ||
70 | dependencies: [libgnunetcore_dep, libgnunetutil_dep, | ||
71 | libgnunetstatistics_dep, | ||
72 | libgnunettransportcore_dep, | ||
73 | zlib_dep], | ||
74 | include_directories: [incdir, configuration_inc], | ||
75 | install: true, | ||
76 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
77 | |||
78 | configure_file(input : 'test_core_defaults.conf', | ||
79 | output : 'test_core_defaults.conf', | ||
80 | copy: true) | ||
81 | |||
82 | configure_file(input : 'test_core_api_send_to_self.conf', | ||
83 | output : 'test_core_api_send_to_self.conf', | ||
84 | copy: true) | ||
85 | |||
86 | configure_file(input : 'test_core_api_peer1.conf', | ||
87 | output : 'test_core_api_peer1.conf', | ||
88 | copy: true) | ||
89 | |||
90 | testcore_api_send_self = executable ('test_core_api_send_to_self', | ||
91 | ['test_core_api_send_to_self.c'], | ||
92 | dependencies: [ | ||
93 | libgnunetcore_dep, | ||
94 | libgnunetutil_dep, | ||
95 | libgnunettesting_dep | ||
96 | ], | ||
97 | include_directories: [incdir, configuration_inc], | ||
98 | install: false) | ||
99 | |||
100 | testcore_api_start = executable ('test_core_api_start_only', | ||
101 | ['test_core_api_send_to_self.c'], | ||
102 | dependencies: [ | ||
103 | libgnunetcore_dep, | ||
104 | libgnunetutil_dep, | ||
105 | libgnunettesting_dep | ||
106 | ], | ||
107 | include_directories: [incdir, configuration_inc], | ||
108 | install: false) | ||
109 | |||
110 | test('test_core_api_send_to_self', testcore_api_send_self, | ||
111 | suite: 'core', workdir: meson.current_build_dir()) | ||
112 | test('test_core_api_start_only', testcore_api_start, | ||
113 | suite: 'core', workdir: meson.current_build_dir()) | ||
diff --git a/src/service/core/test_core_api.c b/src/service/core/test_core_api.c new file mode 100644 index 000000000..653ce1aa0 --- /dev/null +++ b/src/service/core/test_core_api.c | |||
@@ -0,0 +1,348 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2015, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file core/test_core_api.c | ||
22 | * @brief testcase for core_api.c | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_arm_service.h" | ||
27 | #include "gnunet_core_service.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_transport_service.h" | ||
30 | #include "gnunet_transport_hello_service.h" | ||
31 | #include "gnunet_ats_service.h" | ||
32 | |||
33 | #define MTYPE 12345 | ||
34 | |||
35 | struct PeerContext | ||
36 | { | ||
37 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
38 | struct GNUNET_CORE_Handle *ch; | ||
39 | struct GNUNET_PeerIdentity id; | ||
40 | struct GNUNET_TRANSPORT_OfferHelloHandle *oh; | ||
41 | struct GNUNET_TRANSPORT_HelloGetHandle *ghh; | ||
42 | struct GNUNET_ATS_ConnectivityHandle *ats; | ||
43 | struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh; | ||
44 | struct GNUNET_MessageHeader *hello; | ||
45 | int connect_status; | ||
46 | struct GNUNET_OS_Process *arm_proc; | ||
47 | }; | ||
48 | |||
49 | static struct PeerContext p1; | ||
50 | |||
51 | static struct PeerContext p2; | ||
52 | |||
53 | static struct GNUNET_SCHEDULER_Task *err_task; | ||
54 | |||
55 | static int ok; | ||
56 | |||
57 | #define OKPP \ | ||
58 | do \ | ||
59 | { \ | ||
60 | ok++; \ | ||
61 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, \ | ||
62 | "Now at stage %u at %s:%u\n", \ | ||
63 | ok, \ | ||
64 | __FILE__, \ | ||
65 | __LINE__); \ | ||
66 | } while (0) | ||
67 | |||
68 | |||
69 | static void | ||
70 | offer_hello_done (void *cls) | ||
71 | { | ||
72 | struct PeerContext *p = cls; | ||
73 | |||
74 | p->oh = NULL; | ||
75 | } | ||
76 | |||
77 | |||
78 | static void | ||
79 | process_hello (void *cls, const struct GNUNET_MessageHeader *message) | ||
80 | { | ||
81 | struct PeerContext *p = cls; | ||
82 | |||
83 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
84 | "Received (my) HELLO from transport service\n"); | ||
85 | GNUNET_assert (message != NULL); | ||
86 | if ((p == &p1) && (NULL == p2.oh)) | ||
87 | p2.oh = | ||
88 | GNUNET_TRANSPORT_offer_hello (p2.cfg, message, &offer_hello_done, &p2); | ||
89 | if ((p == &p2) && (NULL == p1.oh)) | ||
90 | p1.oh = | ||
91 | GNUNET_TRANSPORT_offer_hello (p1.cfg, message, &offer_hello_done, &p1); | ||
92 | } | ||
93 | |||
94 | |||
95 | static void | ||
96 | terminate_peer (struct PeerContext *p) | ||
97 | { | ||
98 | if (NULL != p->ch) | ||
99 | { | ||
100 | GNUNET_CORE_disconnect (p->ch); | ||
101 | p->ch = NULL; | ||
102 | } | ||
103 | if (NULL != p->ghh) | ||
104 | { | ||
105 | GNUNET_TRANSPORT_hello_get_cancel (p->ghh); | ||
106 | p->ghh = NULL; | ||
107 | } | ||
108 | if (NULL != p->oh) | ||
109 | { | ||
110 | GNUNET_TRANSPORT_offer_hello_cancel (p->oh); | ||
111 | p->oh = NULL; | ||
112 | } | ||
113 | if (NULL != p->ats_sh) | ||
114 | { | ||
115 | GNUNET_ATS_connectivity_suggest_cancel (p->ats_sh); | ||
116 | p->ats_sh = NULL; | ||
117 | } | ||
118 | if (NULL != p->ats) | ||
119 | { | ||
120 | GNUNET_ATS_connectivity_done (p->ats); | ||
121 | p->ats = NULL; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | |||
126 | static void | ||
127 | terminate_task (void *cls) | ||
128 | { | ||
129 | GNUNET_assert (ok == 6); | ||
130 | terminate_peer (&p1); | ||
131 | terminate_peer (&p2); | ||
132 | ok = 0; | ||
133 | } | ||
134 | |||
135 | |||
136 | static void | ||
137 | terminate_task_error (void *cls) | ||
138 | { | ||
139 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ENDING ANGRILY %u\n", ok); | ||
140 | GNUNET_break (0); | ||
141 | terminate_peer (&p1); | ||
142 | terminate_peer (&p2); | ||
143 | ok = 42; | ||
144 | } | ||
145 | |||
146 | |||
147 | static void * | ||
148 | connect_notify (void *cls, | ||
149 | const struct GNUNET_PeerIdentity *peer, | ||
150 | struct GNUNET_MQ_Handle *mq) | ||
151 | { | ||
152 | struct PeerContext *pc = cls; | ||
153 | struct GNUNET_MQ_Envelope *env; | ||
154 | struct GNUNET_MessageHeader *msg; | ||
155 | |||
156 | if (0 == memcmp (&pc->id, peer, sizeof(struct GNUNET_PeerIdentity))) | ||
157 | return (void *) peer; | ||
158 | GNUNET_assert (pc->connect_status == 0); | ||
159 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
160 | "Encrypted connection established to peer `%s'\n", | ||
161 | GNUNET_i2s (peer)); | ||
162 | pc->connect_status = 1; | ||
163 | if (pc == &p1) | ||
164 | { | ||
165 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
166 | "Asking core (1) for transmission to peer `%s'\n", | ||
167 | GNUNET_i2s (&p2.id)); | ||
168 | env = GNUNET_MQ_msg (msg, MTYPE); | ||
169 | /* enable corking for this test */ | ||
170 | GNUNET_MQ_env_set_options (env, | ||
171 | GNUNET_MQ_PRIO_BEST_EFFORT | ||
172 | | GNUNET_MQ_PREF_CORK_ALLOWED); | ||
173 | /* now actually transmit message */ | ||
174 | GNUNET_assert (ok == 4); | ||
175 | OKPP; | ||
176 | GNUNET_MQ_send (mq, env); | ||
177 | } | ||
178 | return (void *) peer; | ||
179 | } | ||
180 | |||
181 | |||
182 | static void | ||
183 | disconnect_notify (void *cls, | ||
184 | const struct GNUNET_PeerIdentity *peer, | ||
185 | void *internal_cls) | ||
186 | { | ||
187 | struct PeerContext *pc = cls; | ||
188 | |||
189 | if (0 == memcmp (&pc->id, peer, sizeof(struct GNUNET_PeerIdentity))) | ||
190 | return; | ||
191 | pc->connect_status = 0; | ||
192 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
193 | "Encrypted connection to `%s' cut\n", | ||
194 | GNUNET_i2s (peer)); | ||
195 | } | ||
196 | |||
197 | |||
198 | static void | ||
199 | handle_test (void *cls, const struct GNUNET_MessageHeader *message) | ||
200 | { | ||
201 | const struct GNUNET_PeerIdentity *peer = cls; | ||
202 | |||
203 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
204 | "Receiving message from `%s'.\n", | ||
205 | GNUNET_i2s (peer)); | ||
206 | GNUNET_assert (ok == 5); | ||
207 | OKPP; | ||
208 | GNUNET_SCHEDULER_cancel (err_task); | ||
209 | err_task = GNUNET_SCHEDULER_add_now (&terminate_task, NULL); | ||
210 | } | ||
211 | |||
212 | |||
213 | static void | ||
214 | init_notify (void *cls, const struct GNUNET_PeerIdentity *my_identity) | ||
215 | { | ||
216 | struct PeerContext *p = cls; | ||
217 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
218 | { GNUNET_MQ_hd_fixed_size (test, MTYPE, struct GNUNET_MessageHeader, NULL), | ||
219 | GNUNET_MQ_handler_end () }; | ||
220 | |||
221 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
222 | "Core connection to `%s' established\n", | ||
223 | GNUNET_i2s (my_identity)); | ||
224 | p->id = *my_identity; | ||
225 | if (cls == &p1) | ||
226 | { | ||
227 | GNUNET_assert (ok == 2); | ||
228 | OKPP; | ||
229 | /* connect p2 */ | ||
230 | p2.ch = GNUNET_CORE_connect (p2.cfg, | ||
231 | &p2, | ||
232 | &init_notify, | ||
233 | &connect_notify, | ||
234 | &disconnect_notify, | ||
235 | handlers); | ||
236 | } | ||
237 | else | ||
238 | { | ||
239 | GNUNET_assert (ok == 3); | ||
240 | OKPP; | ||
241 | GNUNET_assert (cls == &p2); | ||
242 | p1.ats_sh = GNUNET_ATS_connectivity_suggest (p1.ats, &p2.id, 1); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | |||
247 | static void | ||
248 | setup_peer (struct PeerContext *p, const char *cfgname) | ||
249 | { | ||
250 | char *binary; | ||
251 | |||
252 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm"); | ||
253 | p->cfg = GNUNET_CONFIGURATION_create (); | ||
254 | p->arm_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
255 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
256 | NULL, | ||
257 | NULL, | ||
258 | NULL, | ||
259 | binary, | ||
260 | "gnunet-service-arm", | ||
261 | "-c", | ||
262 | cfgname, | ||
263 | NULL); | ||
264 | GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); | ||
265 | p->ats = GNUNET_ATS_connectivity_init (p->cfg); | ||
266 | GNUNET_assert (NULL != p->ats); | ||
267 | p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg, | ||
268 | GNUNET_TRANSPORT_AC_ANY, | ||
269 | &process_hello, | ||
270 | p); | ||
271 | GNUNET_free (binary); | ||
272 | } | ||
273 | |||
274 | |||
275 | static void | ||
276 | run (void *cls, | ||
277 | char *const *args, | ||
278 | const char *cfgfile, | ||
279 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
280 | { | ||
281 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
282 | { GNUNET_MQ_hd_fixed_size (test, MTYPE, struct GNUNET_MessageHeader, NULL), | ||
283 | GNUNET_MQ_handler_end () }; | ||
284 | |||
285 | GNUNET_assert (ok == 1); | ||
286 | OKPP; | ||
287 | setup_peer (&p1, "test_core_api_peer1.conf"); | ||
288 | setup_peer (&p2, "test_core_api_peer2.conf"); | ||
289 | err_task = GNUNET_SCHEDULER_add_delayed ( | ||
290 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300), | ||
291 | &terminate_task_error, | ||
292 | NULL); | ||
293 | p1.ch = GNUNET_CORE_connect (p1.cfg, | ||
294 | &p1, | ||
295 | &init_notify, | ||
296 | &connect_notify, | ||
297 | &disconnect_notify, | ||
298 | handlers); | ||
299 | } | ||
300 | |||
301 | |||
302 | static void | ||
303 | stop_arm (struct PeerContext *p) | ||
304 | { | ||
305 | if (0 != GNUNET_OS_process_kill (p->arm_proc, GNUNET_TERM_SIG)) | ||
306 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
307 | if (GNUNET_OK != GNUNET_OS_process_wait (p->arm_proc)) | ||
308 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); | ||
309 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
310 | "ARM process %u stopped\n", | ||
311 | GNUNET_OS_process_get_pid (p->arm_proc)); | ||
312 | GNUNET_OS_process_destroy (p->arm_proc); | ||
313 | p->arm_proc = NULL; | ||
314 | GNUNET_CONFIGURATION_destroy (p->cfg); | ||
315 | } | ||
316 | |||
317 | |||
318 | int | ||
319 | main (int argc, char *argv1[]) | ||
320 | { | ||
321 | char *const argv[] = { "test-core-api", "-c", "test_core_api_data.conf", | ||
322 | NULL }; | ||
323 | struct GNUNET_GETOPT_CommandLineOption options[] = | ||
324 | { GNUNET_GETOPT_OPTION_END }; | ||
325 | |||
326 | ok = 1; | ||
327 | GNUNET_log_setup ("test-core-api", "WARNING", NULL); | ||
328 | GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1, | ||
329 | argv, | ||
330 | "test-core-api", | ||
331 | "nohelp", | ||
332 | options, | ||
333 | &run, | ||
334 | &ok); | ||
335 | stop_arm (&p1); | ||
336 | stop_arm (&p2); | ||
337 | GNUNET_DISK_purge_cfg_dir | ||
338 | ("test_core_api_peer1.conf", | ||
339 | "GNUNET_TEST_HOME"); | ||
340 | GNUNET_DISK_purge_cfg_dir | ||
341 | ("test_core_api_peer2.conf", | ||
342 | "GNUNET_TEST_HOME"); | ||
343 | |||
344 | return ok; | ||
345 | } | ||
346 | |||
347 | |||
348 | /* end of test_core_api.c */ | ||
diff --git a/src/service/core/test_core_api_data.conf b/src/service/core/test_core_api_data.conf new file mode 100644 index 000000000..420849ba9 --- /dev/null +++ b/src/service/core/test_core_api_data.conf | |||
@@ -0,0 +1,11 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | |||
4 | [ats] | ||
5 | WAN_QUOTA_IN = 64 kiB | ||
6 | WAN_QUOTA_OUT = 64 kiB | ||
7 | |||
8 | [core] | ||
9 | PORT = 52092 | ||
10 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-core.sock | ||
11 | |||
diff --git a/src/service/core/test_core_api_peer1.conf b/src/service/core/test_core_api_peer1.conf new file mode 100644 index 000000000..c3e8fb86c --- /dev/null +++ b/src/service/core/test_core_api_peer1.conf | |||
@@ -0,0 +1,38 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-1/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 12460 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 12461 | ||
11 | |||
12 | [resolver] | ||
13 | PORT = 12462 | ||
14 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock | ||
15 | |||
16 | [peerstore] | ||
17 | PORT = 12463 | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerstore.sock | ||
19 | |||
20 | [transport] | ||
21 | PORT = 12464 | ||
22 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock | ||
23 | |||
24 | [core] | ||
25 | PORT = 12475 | ||
26 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-core.sock | ||
27 | |||
28 | [communicator-tcp] | ||
29 | PORT = 12467 | ||
30 | |||
31 | [communicator-udp] | ||
32 | PORT = 12468 | ||
33 | |||
34 | [transport-unix] | ||
35 | PORT = 12469 | ||
36 | |||
37 | [transport-http] | ||
38 | PORT = 12470 | ||
diff --git a/src/service/core/test_core_api_peer2.conf b/src/service/core/test_core_api_peer2.conf new file mode 100644 index 000000000..7cea78ae4 --- /dev/null +++ b/src/service/core/test_core_api_peer2.conf | |||
@@ -0,0 +1,39 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-2/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 22460 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 22461 | ||
11 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock | ||
12 | |||
13 | [resolver] | ||
14 | PORT = 22462 | ||
15 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock | ||
16 | |||
17 | [peerstore] | ||
18 | PORT = 22463 | ||
19 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerstore.sock | ||
20 | |||
21 | [transport] | ||
22 | PORT = 22464 | ||
23 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock | ||
24 | |||
25 | [core] | ||
26 | PORT = 22475 | ||
27 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-core.sock | ||
28 | |||
29 | [communicator-tcp] | ||
30 | PORT = 22467 | ||
31 | |||
32 | [communicator-udp] | ||
33 | PORT = 22468 | ||
34 | |||
35 | [transport-unix] | ||
36 | PORT = 22469 | ||
37 | |||
38 | [transport-http] | ||
39 | PORT = 22470 | ||
diff --git a/src/service/core/test_core_api_reliability.c b/src/service/core/test_core_api_reliability.c new file mode 100644 index 000000000..d4b55a6b8 --- /dev/null +++ b/src/service/core/test_core_api_reliability.c | |||
@@ -0,0 +1,537 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2015, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file core/test_core_api_reliability.c | ||
22 | * @brief testcase for core_api.c focusing on reliable transmission (with TCP) | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_arm_service.h" | ||
27 | #include "gnunet_core_service.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_ats_service.h" | ||
30 | #include "gnunet_transport_service.h" | ||
31 | #include "gnunet_transport_hello_service.h" | ||
32 | #include <gauger.h> | ||
33 | |||
34 | /** | ||
35 | * Note that this value must not significantly exceed | ||
36 | * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise | ||
37 | * messages may be dropped even for a reliable transport. | ||
38 | */ | ||
39 | #define TOTAL_MSGS (600 * 10) | ||
40 | |||
41 | /** | ||
42 | * How long until we give up on transmitting the message? | ||
43 | */ | ||
44 | #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) | ||
45 | |||
46 | #define MTYPE 12345 | ||
47 | |||
48 | |||
49 | static unsigned long long total_bytes; | ||
50 | |||
51 | static struct GNUNET_TIME_Absolute start_time; | ||
52 | |||
53 | static struct GNUNET_SCHEDULER_Task *err_task; | ||
54 | |||
55 | |||
56 | struct PeerContext | ||
57 | { | ||
58 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
59 | struct GNUNET_CORE_Handle *ch; | ||
60 | struct GNUNET_MQ_Handle *mq; | ||
61 | struct GNUNET_PeerIdentity id; | ||
62 | struct GNUNET_TRANSPORT_OfferHelloHandle *oh; | ||
63 | struct GNUNET_MessageHeader *hello; | ||
64 | struct GNUNET_TRANSPORT_HelloGetHandle *ghh; | ||
65 | struct GNUNET_ATS_ConnectivityHandle *ats; | ||
66 | struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh; | ||
67 | int connect_status; | ||
68 | struct GNUNET_OS_Process *arm_proc; | ||
69 | }; | ||
70 | |||
71 | static struct PeerContext p1; | ||
72 | |||
73 | static struct PeerContext p2; | ||
74 | |||
75 | static int ok; | ||
76 | |||
77 | static int32_t tr_n; | ||
78 | |||
79 | |||
80 | #define OKPP do { ok++; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, \ | ||
81 | "Now at stage %u at %s:%u\n", ok, __FILE__, \ | ||
82 | __LINE__); } while (0) | ||
83 | |||
84 | struct TestMessage | ||
85 | { | ||
86 | struct GNUNET_MessageHeader header; | ||
87 | uint32_t num GNUNET_PACKED; | ||
88 | }; | ||
89 | |||
90 | |||
91 | static unsigned int | ||
92 | get_size (unsigned int iter) | ||
93 | { | ||
94 | unsigned int ret; | ||
95 | |||
96 | if (iter < 60000) | ||
97 | return iter + sizeof(struct TestMessage); | ||
98 | ret = (iter * iter * iter); | ||
99 | return sizeof(struct TestMessage) + (ret % 60000); | ||
100 | } | ||
101 | |||
102 | |||
103 | static void | ||
104 | terminate_peer (struct PeerContext *p) | ||
105 | { | ||
106 | if (NULL != p->ch) | ||
107 | { | ||
108 | GNUNET_CORE_disconnect (p->ch); | ||
109 | p->ch = NULL; | ||
110 | } | ||
111 | if (NULL != p->ghh) | ||
112 | { | ||
113 | GNUNET_TRANSPORT_hello_get_cancel (p->ghh); | ||
114 | p->ghh = NULL; | ||
115 | } | ||
116 | if (NULL != p->oh) | ||
117 | { | ||
118 | GNUNET_TRANSPORT_offer_hello_cancel (p->oh); | ||
119 | p->oh = NULL; | ||
120 | } | ||
121 | if (NULL != p->ats_sh) | ||
122 | { | ||
123 | GNUNET_ATS_connectivity_suggest_cancel (p->ats_sh); | ||
124 | p->ats_sh = NULL; | ||
125 | } | ||
126 | if (NULL != p->ats) | ||
127 | { | ||
128 | GNUNET_ATS_connectivity_done (p->ats); | ||
129 | p->ats = NULL; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | |||
134 | static void | ||
135 | terminate_task_error (void *cls) | ||
136 | { | ||
137 | err_task = NULL; | ||
138 | GNUNET_break (0); | ||
139 | GNUNET_SCHEDULER_shutdown (); | ||
140 | ok = 42; | ||
141 | } | ||
142 | |||
143 | |||
144 | static void | ||
145 | do_shutdown (void *cls) | ||
146 | { | ||
147 | unsigned long long delta; | ||
148 | |||
149 | delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value_us; | ||
150 | if (0 == delta) | ||
151 | delta = 1; | ||
152 | fprintf (stderr, | ||
153 | "\nThroughput was %llu kb/s\n", | ||
154 | total_bytes * 1000000LL / 1024 / delta); | ||
155 | GAUGER ("CORE", | ||
156 | "Core throughput/s", | ||
157 | total_bytes * 1000000LL / 1024 / delta, | ||
158 | "kb/s"); | ||
159 | if (NULL != err_task) | ||
160 | { | ||
161 | GNUNET_SCHEDULER_cancel (err_task); | ||
162 | err_task = NULL; | ||
163 | } | ||
164 | terminate_peer (&p1); | ||
165 | terminate_peer (&p2); | ||
166 | } | ||
167 | |||
168 | |||
169 | static void | ||
170 | send_message (struct GNUNET_MQ_Handle *mq, | ||
171 | int32_t num) | ||
172 | { | ||
173 | struct GNUNET_MQ_Envelope *env; | ||
174 | struct TestMessage *hdr; | ||
175 | unsigned int s; | ||
176 | |||
177 | GNUNET_assert (NULL != mq); | ||
178 | GNUNET_assert (tr_n < TOTAL_MSGS); | ||
179 | s = get_size (tr_n); | ||
180 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
181 | "Sending message %u of size %u\n", | ||
182 | tr_n, | ||
183 | s); | ||
184 | env = GNUNET_MQ_msg_extra (hdr, | ||
185 | s - sizeof(struct TestMessage), | ||
186 | MTYPE); | ||
187 | hdr->num = htonl (tr_n); | ||
188 | memset (&hdr[1], | ||
189 | tr_n, | ||
190 | s - sizeof(struct TestMessage)); | ||
191 | tr_n++; | ||
192 | GNUNET_SCHEDULER_cancel (err_task); | ||
193 | err_task = | ||
194 | GNUNET_SCHEDULER_add_delayed (TIMEOUT, | ||
195 | &terminate_task_error, | ||
196 | NULL); | ||
197 | total_bytes += s; | ||
198 | GNUNET_MQ_send (mq, | ||
199 | env); | ||
200 | } | ||
201 | |||
202 | |||
203 | static void * | ||
204 | connect_notify (void *cls, | ||
205 | const struct GNUNET_PeerIdentity *peer, | ||
206 | struct GNUNET_MQ_Handle *mq) | ||
207 | { | ||
208 | struct PeerContext *pc = cls; | ||
209 | |||
210 | if (0 == memcmp (&pc->id, | ||
211 | peer, | ||
212 | sizeof(struct GNUNET_PeerIdentity))) | ||
213 | return (void *) peer; | ||
214 | pc->mq = mq; | ||
215 | GNUNET_assert (0 == pc->connect_status); | ||
216 | pc->connect_status = 1; | ||
217 | if (pc == &p1) | ||
218 | { | ||
219 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
220 | "Encrypted connection established to peer `%s'\n", | ||
221 | GNUNET_i2s (peer)); | ||
222 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
223 | "Asking core (1) for transmission to peer `%s'\n", | ||
224 | GNUNET_i2s (&p2.id)); | ||
225 | GNUNET_SCHEDULER_cancel (err_task); | ||
226 | err_task = | ||
227 | GNUNET_SCHEDULER_add_delayed (TIMEOUT, | ||
228 | &terminate_task_error, | ||
229 | NULL); | ||
230 | start_time = GNUNET_TIME_absolute_get (); | ||
231 | send_message (mq, | ||
232 | 0); | ||
233 | } | ||
234 | return (void *) peer; | ||
235 | } | ||
236 | |||
237 | |||
238 | static void | ||
239 | disconnect_notify (void *cls, | ||
240 | const struct GNUNET_PeerIdentity *peer, | ||
241 | void *internal_cls) | ||
242 | { | ||
243 | struct PeerContext *pc = cls; | ||
244 | |||
245 | if (0 == memcmp (&pc->id, | ||
246 | peer, | ||
247 | sizeof(struct GNUNET_PeerIdentity))) | ||
248 | return; | ||
249 | pc->mq = NULL; | ||
250 | pc->connect_status = 0; | ||
251 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
252 | "Encrypted connection to `%s' cut\n", | ||
253 | GNUNET_i2s (peer)); | ||
254 | } | ||
255 | |||
256 | |||
257 | static int | ||
258 | check_test (void *cls, | ||
259 | const struct TestMessage *hdr) | ||
260 | { | ||
261 | return GNUNET_OK; /* accept all */ | ||
262 | } | ||
263 | |||
264 | |||
265 | static void | ||
266 | handle_test (void *cls, | ||
267 | const struct TestMessage *hdr) | ||
268 | { | ||
269 | static int n; | ||
270 | unsigned int s; | ||
271 | |||
272 | s = get_size (n); | ||
273 | if (ntohs (hdr->header.size) != s) | ||
274 | { | ||
275 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
276 | "Expected message %u of size %u, got %u bytes of message %u\n", | ||
277 | n, | ||
278 | s, | ||
279 | ntohs (hdr->header.size), | ||
280 | ntohl (hdr->num)); | ||
281 | GNUNET_SCHEDULER_cancel (err_task); | ||
282 | err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error, | ||
283 | NULL); | ||
284 | return; | ||
285 | } | ||
286 | if (ntohl (hdr->num) != n) | ||
287 | { | ||
288 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
289 | "Expected message %u of size %u, got %u bytes of message %u\n", | ||
290 | n, | ||
291 | s, | ||
292 | (unsigned int) ntohs (hdr->header.size), | ||
293 | (unsigned int) ntohl (hdr->num)); | ||
294 | GNUNET_SCHEDULER_cancel (err_task); | ||
295 | err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error, | ||
296 | NULL); | ||
297 | return; | ||
298 | } | ||
299 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
300 | "Got message %u of size %u\n", | ||
301 | (unsigned int) ntohl (hdr->num), | ||
302 | (unsigned int) ntohs (hdr->header.size)); | ||
303 | n++; | ||
304 | if (0 == (n % (TOTAL_MSGS / 100))) | ||
305 | fprintf (stderr, | ||
306 | "%s", | ||
307 | "."); | ||
308 | if (n == TOTAL_MSGS) | ||
309 | { | ||
310 | ok = 0; | ||
311 | GNUNET_SCHEDULER_shutdown (); | ||
312 | } | ||
313 | else | ||
314 | { | ||
315 | if (n == tr_n) | ||
316 | { | ||
317 | send_message (p1.mq, | ||
318 | tr_n); | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | |||
324 | static void | ||
325 | init_notify (void *cls, | ||
326 | const struct GNUNET_PeerIdentity *my_identity) | ||
327 | { | ||
328 | struct PeerContext *p = cls; | ||
329 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
330 | GNUNET_MQ_hd_var_size (test, | ||
331 | MTYPE, | ||
332 | struct TestMessage, | ||
333 | NULL), | ||
334 | GNUNET_MQ_handler_end () | ||
335 | }; | ||
336 | |||
337 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
338 | "Connection to CORE service of `%s' established\n", | ||
339 | GNUNET_i2s (my_identity)); | ||
340 | p->id = *my_identity; | ||
341 | if (cls == &p1) | ||
342 | { | ||
343 | GNUNET_assert (ok == 2); | ||
344 | OKPP; | ||
345 | /* connect p2 */ | ||
346 | GNUNET_assert (NULL != | ||
347 | (p2.ch = GNUNET_CORE_connect (p2.cfg, | ||
348 | &p2, | ||
349 | &init_notify, | ||
350 | &connect_notify, | ||
351 | &disconnect_notify, | ||
352 | handlers))); | ||
353 | } | ||
354 | else | ||
355 | { | ||
356 | GNUNET_assert (ok == 3); | ||
357 | OKPP; | ||
358 | GNUNET_assert (cls == &p2); | ||
359 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
360 | "Asking transport (1) to connect to peer `%s'\n", | ||
361 | GNUNET_i2s (&p2.id)); | ||
362 | p1.ats_sh = GNUNET_ATS_connectivity_suggest (p1.ats, | ||
363 | &p2.id, | ||
364 | 1); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | |||
369 | static void | ||
370 | offer_hello_done (void *cls) | ||
371 | { | ||
372 | struct PeerContext *p = cls; | ||
373 | |||
374 | p->oh = NULL; | ||
375 | } | ||
376 | |||
377 | |||
378 | static void | ||
379 | process_hello (void *cls, | ||
380 | const struct GNUNET_MessageHeader *message) | ||
381 | { | ||
382 | struct PeerContext *p = cls; | ||
383 | |||
384 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
385 | "Received (my) `%s' from transport service\n", "HELLO"); | ||
386 | GNUNET_assert (message != NULL); | ||
387 | GNUNET_free (p->hello); | ||
388 | p->hello = GNUNET_copy_message (message); | ||
389 | if ((p == &p1) && (NULL == p2.oh)) | ||
390 | p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg, | ||
391 | message, | ||
392 | &offer_hello_done, | ||
393 | &p2); | ||
394 | if ((p == &p2) && (NULL == p1.oh)) | ||
395 | p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg, | ||
396 | message, | ||
397 | &offer_hello_done, | ||
398 | &p1); | ||
399 | |||
400 | if ((p == &p1) && (p2.hello != NULL) && (NULL == p1.oh)) | ||
401 | p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg, | ||
402 | p2.hello, | ||
403 | &offer_hello_done, | ||
404 | &p1); | ||
405 | if ((p == &p2) && (p1.hello != NULL) && (NULL == p2.oh)) | ||
406 | p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg, | ||
407 | p1.hello, | ||
408 | &offer_hello_done, | ||
409 | &p2); | ||
410 | } | ||
411 | |||
412 | |||
413 | static void | ||
414 | setup_peer (struct PeerContext *p, | ||
415 | const char *cfgname) | ||
416 | { | ||
417 | char *binary; | ||
418 | |||
419 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm"); | ||
420 | p->cfg = GNUNET_CONFIGURATION_create (); | ||
421 | p->arm_proc | ||
422 | = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
423 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
424 | NULL, NULL, NULL, | ||
425 | binary, | ||
426 | "gnunet-service-arm", | ||
427 | "-c", | ||
428 | cfgname, | ||
429 | NULL); | ||
430 | GNUNET_assert (GNUNET_OK == | ||
431 | GNUNET_CONFIGURATION_load (p->cfg, | ||
432 | cfgname)); | ||
433 | p->ats = GNUNET_ATS_connectivity_init (p->cfg); | ||
434 | GNUNET_assert (NULL != p->ats); | ||
435 | p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg, | ||
436 | GNUNET_TRANSPORT_AC_ANY, | ||
437 | &process_hello, | ||
438 | p); | ||
439 | GNUNET_free (binary); | ||
440 | } | ||
441 | |||
442 | |||
443 | static void | ||
444 | run (void *cls, | ||
445 | char *const *args, | ||
446 | const char *cfgfile, | ||
447 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
448 | { | ||
449 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
450 | GNUNET_MQ_hd_fixed_size (test, | ||
451 | MTYPE, | ||
452 | struct TestMessage, | ||
453 | NULL), | ||
454 | GNUNET_MQ_handler_end () | ||
455 | }; | ||
456 | |||
457 | GNUNET_assert (ok == 1); | ||
458 | OKPP; | ||
459 | setup_peer (&p1, | ||
460 | "test_core_api_peer1.conf"); | ||
461 | setup_peer (&p2, | ||
462 | "test_core_api_peer2.conf"); | ||
463 | err_task = | ||
464 | GNUNET_SCHEDULER_add_delayed (TIMEOUT, | ||
465 | &terminate_task_error, | ||
466 | NULL); | ||
467 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
468 | NULL); | ||
469 | |||
470 | GNUNET_assert (NULL != | ||
471 | (p1.ch = GNUNET_CORE_connect (p1.cfg, | ||
472 | &p1, | ||
473 | &init_notify, | ||
474 | &connect_notify, | ||
475 | &disconnect_notify, | ||
476 | handlers))); | ||
477 | } | ||
478 | |||
479 | |||
480 | static void | ||
481 | stop_arm (struct PeerContext *p) | ||
482 | { | ||
483 | if (0 != GNUNET_OS_process_kill (p->arm_proc, | ||
484 | GNUNET_TERM_SIG)) | ||
485 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
486 | "kill"); | ||
487 | if (GNUNET_OK != GNUNET_OS_process_wait (p->arm_proc)) | ||
488 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
489 | "waitpid"); | ||
490 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
491 | "ARM process %u stopped\n", | ||
492 | GNUNET_OS_process_get_pid (p->arm_proc)); | ||
493 | GNUNET_OS_process_destroy (p->arm_proc); | ||
494 | p->arm_proc = NULL; | ||
495 | GNUNET_CONFIGURATION_destroy (p->cfg); | ||
496 | } | ||
497 | |||
498 | |||
499 | int | ||
500 | main (int argc, | ||
501 | char *argv1[]) | ||
502 | { | ||
503 | char *const argv[] = { | ||
504 | "test-core-api-reliability", | ||
505 | "-c", | ||
506 | "test_core_api_data.conf", | ||
507 | NULL | ||
508 | }; | ||
509 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
510 | GNUNET_GETOPT_OPTION_END | ||
511 | }; | ||
512 | |||
513 | ok = 1; | ||
514 | GNUNET_log_setup ("test-core-api-reliability", | ||
515 | "WARNING", | ||
516 | NULL); | ||
517 | GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1, | ||
518 | argv, | ||
519 | "test-core-api-reliability", | ||
520 | "nohelp", | ||
521 | options, | ||
522 | &run, | ||
523 | &ok); | ||
524 | stop_arm (&p1); | ||
525 | stop_arm (&p2); | ||
526 | GNUNET_free (p1.hello); | ||
527 | GNUNET_free (p2.hello); | ||
528 | GNUNET_DISK_purge_cfg_dir ("test_core_api_peer1.conf", | ||
529 | "GNUNET_TEST_HOME"); | ||
530 | GNUNET_DISK_purge_cfg_dir ("test_core_api_peer2.conf", | ||
531 | "GNUNET_TEST_HOME"); | ||
532 | |||
533 | return ok; | ||
534 | } | ||
535 | |||
536 | |||
537 | /* end of test_core_api_reliability.c */ | ||
diff --git a/src/service/core/test_core_api_send_to_self.c b/src/service/core/test_core_api_send_to_self.c new file mode 100644 index 000000000..c2e39cd26 --- /dev/null +++ b/src/service/core/test_core_api_send_to_self.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2010, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file core/test_core_api_send_to_self.c | ||
23 | * @brief test that sending a message to ourselves via CORE works | ||
24 | * @author Philipp Toelke | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_testing_lib.h" | ||
30 | #include "gnunet_protocols.h" | ||
31 | #include "gnunet_core_service.h" | ||
32 | #include "gnunet_constants.h" | ||
33 | |||
34 | /** | ||
35 | * Final status code. | ||
36 | */ | ||
37 | static int ret; | ||
38 | |||
39 | /** | ||
40 | * Handle to the cleanup task. | ||
41 | */ | ||
42 | static struct GNUNET_SCHEDULER_Task *die_task; | ||
43 | |||
44 | /** | ||
45 | * Identity of this peer. | ||
46 | */ | ||
47 | static struct GNUNET_PeerIdentity myself; | ||
48 | |||
49 | /** | ||
50 | * The handle to core | ||
51 | */ | ||
52 | static struct GNUNET_CORE_Handle *core; | ||
53 | |||
54 | |||
55 | /** | ||
56 | * Function scheduled as very last function, cleans up after us | ||
57 | */ | ||
58 | static void | ||
59 | cleanup (void *cls) | ||
60 | { | ||
61 | if (NULL != die_task) | ||
62 | { | ||
63 | GNUNET_SCHEDULER_cancel (die_task); | ||
64 | die_task = NULL; | ||
65 | } | ||
66 | if (NULL != core) | ||
67 | { | ||
68 | GNUNET_CORE_disconnect (core); | ||
69 | core = NULL; | ||
70 | } | ||
71 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
72 | "Ending test.\n"); | ||
73 | } | ||
74 | |||
75 | |||
76 | /** | ||
77 | * Function scheduled as very last function, cleans up after us | ||
78 | */ | ||
79 | static void | ||
80 | do_timeout (void *cls) | ||
81 | { | ||
82 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
83 | "Test timeout.\n"); | ||
84 | die_task = NULL; | ||
85 | GNUNET_SCHEDULER_shutdown (); | ||
86 | } | ||
87 | |||
88 | |||
89 | static void | ||
90 | handle_test (void *cls, | ||
91 | const struct GNUNET_MessageHeader *message) | ||
92 | { | ||
93 | GNUNET_SCHEDULER_shutdown (); | ||
94 | ret = 0; | ||
95 | } | ||
96 | |||
97 | |||
98 | static void | ||
99 | init (void *cls, | ||
100 | const struct GNUNET_PeerIdentity *my_identity) | ||
101 | { | ||
102 | if (NULL == my_identity) | ||
103 | { | ||
104 | GNUNET_break (0); | ||
105 | return; | ||
106 | } | ||
107 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
108 | "Correctly connected to CORE; we are the peer %s.\n", | ||
109 | GNUNET_i2s (my_identity)); | ||
110 | GNUNET_memcpy (&myself, | ||
111 | my_identity, | ||
112 | sizeof(struct GNUNET_PeerIdentity)); | ||
113 | } | ||
114 | |||
115 | |||
116 | static void * | ||
117 | connect_cb (void *cls, | ||
118 | const struct GNUNET_PeerIdentity *peer, | ||
119 | struct GNUNET_MQ_Handle *mq) | ||
120 | { | ||
121 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
122 | "Connected to peer %s.\n", | ||
123 | GNUNET_i2s (peer)); | ||
124 | if (0 == memcmp (peer, | ||
125 | &myself, | ||
126 | sizeof(struct GNUNET_PeerIdentity))) | ||
127 | { | ||
128 | struct GNUNET_MQ_Envelope *env; | ||
129 | struct GNUNET_MessageHeader *msg; | ||
130 | |||
131 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
132 | "Connected to myself; sending message!\n"); | ||
133 | env = GNUNET_MQ_msg (msg, | ||
134 | GNUNET_MESSAGE_TYPE_DUMMY); | ||
135 | GNUNET_MQ_send (mq, | ||
136 | env); | ||
137 | } | ||
138 | return NULL; | ||
139 | } | ||
140 | |||
141 | |||
142 | /** | ||
143 | * Main function that will be run by the scheduler. | ||
144 | * | ||
145 | * @param cls closure | ||
146 | * @param cfg configuration | ||
147 | */ | ||
148 | static void | ||
149 | run (void *cls, | ||
150 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
151 | struct GNUNET_TESTING_Peer *peer) | ||
152 | { | ||
153 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
154 | GNUNET_MQ_hd_fixed_size (test, | ||
155 | GNUNET_MESSAGE_TYPE_DUMMY, | ||
156 | struct GNUNET_MessageHeader, | ||
157 | NULL), | ||
158 | GNUNET_MQ_handler_end () | ||
159 | }; | ||
160 | |||
161 | core = | ||
162 | GNUNET_CORE_connect (cfg, | ||
163 | NULL, | ||
164 | &init, | ||
165 | &connect_cb, | ||
166 | NULL, | ||
167 | handlers); | ||
168 | GNUNET_SCHEDULER_add_shutdown (&cleanup, | ||
169 | NULL); | ||
170 | die_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, | ||
171 | &do_timeout, | ||
172 | NULL); | ||
173 | } | ||
174 | |||
175 | |||
176 | /** | ||
177 | * The main function to test sending a message to the local peer via core | ||
178 | * | ||
179 | * @param argc number of arguments from the command line | ||
180 | * @param argv command line arguments | ||
181 | * @return 0 ok, 1 on error | ||
182 | */ | ||
183 | int | ||
184 | main (int argc, char *argv[]) | ||
185 | { | ||
186 | ret = 1; | ||
187 | if (0 != GNUNET_TESTING_peer_run ("test-core-api-send-to-self", | ||
188 | "test_core_api_peer1.conf", | ||
189 | &run, NULL)) | ||
190 | return 1; | ||
191 | return ret; | ||
192 | } | ||
193 | |||
194 | |||
195 | /* end of test_core_api_send_to_self.c */ | ||
diff --git a/src/service/core/test_core_api_send_to_self.conf b/src/service/core/test_core_api_send_to_self.conf new file mode 100644 index 000000000..fe8e69999 --- /dev/null +++ b/src/service/core/test_core_api_send_to_self.conf | |||
@@ -0,0 +1,19 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-core-api-send-to-self/ | ||
4 | |||
5 | [ats] | ||
6 | WAN_QUOTA_IN = 104857600 | ||
7 | WAN_QUOTA_OUT = 104757600 | ||
8 | |||
9 | [test-sts] | ||
10 | IMMEDIATE_START = YES | ||
11 | PORT = 59252 | ||
12 | HOSTNAME = localhost | ||
13 | BINARY = test_core_api_send_to_self | ||
14 | ACCEPT_FROM = 127.0.0.1; | ||
15 | ACCEPT_FROM6 = ::1; | ||
16 | TOTAL_QUOTA_IN = 65536 | ||
17 | TOTAL_QUOTA_OUT = 65536 | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-sts.sock | ||
19 | |||
diff --git a/src/service/core/test_core_api_start_only.c b/src/service/core/test_core_api_start_only.c new file mode 100644 index 000000000..e50d3b2ec --- /dev/null +++ b/src/service/core/test_core_api_start_only.c | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file transport/test_core_api_start_only.c | ||
22 | * @brief testcase for core_api.c that only starts two peers, | ||
23 | * connects to the core service and shuts down again | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_arm_service.h" | ||
28 | #include "gnunet_core_service.h" | ||
29 | #include "gnunet_util_lib.h" | ||
30 | |||
31 | #define TIMEOUT 5 | ||
32 | |||
33 | #define MTYPE 12345 | ||
34 | |||
35 | struct PeerContext | ||
36 | { | ||
37 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
38 | struct GNUNET_CORE_Handle *ch; | ||
39 | struct GNUNET_PeerIdentity id; | ||
40 | struct GNUNET_OS_Process *arm_proc; | ||
41 | }; | ||
42 | |||
43 | static struct PeerContext p1; | ||
44 | |||
45 | static struct PeerContext p2; | ||
46 | |||
47 | static struct GNUNET_SCHEDULER_Task *timeout_task_id; | ||
48 | |||
49 | static int ok; | ||
50 | |||
51 | |||
52 | static void * | ||
53 | connect_notify (void *cls, | ||
54 | const struct GNUNET_PeerIdentity *peer, | ||
55 | struct GNUNET_MQ_Handle *mq) | ||
56 | { | ||
57 | return NULL; | ||
58 | } | ||
59 | |||
60 | |||
61 | static void | ||
62 | disconnect_notify (void *cls, | ||
63 | const struct GNUNET_PeerIdentity *peer, | ||
64 | void *internal_cls) | ||
65 | { | ||
66 | } | ||
67 | |||
68 | |||
69 | static struct GNUNET_MQ_MessageHandler handlers[] = { | ||
70 | GNUNET_MQ_handler_end () | ||
71 | }; | ||
72 | |||
73 | |||
74 | static void | ||
75 | shutdown_task (void *cls) | ||
76 | { | ||
77 | GNUNET_CORE_disconnect (p1.ch); | ||
78 | p1.ch = NULL; | ||
79 | GNUNET_CORE_disconnect (p2.ch); | ||
80 | p2.ch = NULL; | ||
81 | ok = 0; | ||
82 | } | ||
83 | |||
84 | |||
85 | static void | ||
86 | init_notify (void *cls, | ||
87 | const struct GNUNET_PeerIdentity *my_identity) | ||
88 | { | ||
89 | struct PeerContext *p = cls; | ||
90 | |||
91 | if (p == &p1) | ||
92 | { | ||
93 | /* connect p2 */ | ||
94 | p2.ch = GNUNET_CORE_connect (p2.cfg, | ||
95 | &p2, | ||
96 | &init_notify, | ||
97 | &connect_notify, | ||
98 | &disconnect_notify, | ||
99 | handlers); | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | GNUNET_assert (p == &p2); | ||
104 | GNUNET_SCHEDULER_cancel (timeout_task_id); | ||
105 | timeout_task_id = NULL; | ||
106 | GNUNET_SCHEDULER_add_now (&shutdown_task, | ||
107 | NULL); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | |||
112 | static void | ||
113 | setup_peer (struct PeerContext *p, | ||
114 | const char *cfgname) | ||
115 | { | ||
116 | char *binary; | ||
117 | |||
118 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm"); | ||
119 | p->cfg = GNUNET_CONFIGURATION_create (); | ||
120 | p->arm_proc = | ||
121 | GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
122 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
123 | NULL, NULL, NULL, | ||
124 | binary, | ||
125 | "gnunet-service-arm", | ||
126 | "-c", cfgname, | ||
127 | NULL); | ||
128 | GNUNET_assert (GNUNET_OK == | ||
129 | GNUNET_CONFIGURATION_load (p->cfg, | ||
130 | cfgname)); | ||
131 | GNUNET_free (binary); | ||
132 | } | ||
133 | |||
134 | |||
135 | static void | ||
136 | timeout_task (void *cls) | ||
137 | { | ||
138 | fprintf (stderr, | ||
139 | "%s", | ||
140 | "Timeout.\n"); | ||
141 | if (NULL != p1.ch) | ||
142 | { | ||
143 | GNUNET_CORE_disconnect (p1.ch); | ||
144 | p1.ch = NULL; | ||
145 | } | ||
146 | if (NULL != p2.ch) | ||
147 | { | ||
148 | GNUNET_CORE_disconnect (p2.ch); | ||
149 | p2.ch = NULL; | ||
150 | } | ||
151 | ok = 42; | ||
152 | } | ||
153 | |||
154 | |||
155 | static void | ||
156 | run (void *cls, | ||
157 | char *const *args, | ||
158 | const char *cfgfile, | ||
159 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
160 | { | ||
161 | GNUNET_assert (ok == 1); | ||
162 | ok++; | ||
163 | setup_peer (&p1, "test_core_api_peer1.conf"); | ||
164 | setup_peer (&p2, "test_core_api_peer2.conf"); | ||
165 | timeout_task_id = | ||
166 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
167 | (GNUNET_TIME_UNIT_MINUTES, | ||
168 | TIMEOUT), | ||
169 | &timeout_task, | ||
170 | NULL); | ||
171 | p1.ch = GNUNET_CORE_connect (p1.cfg, | ||
172 | &p1, | ||
173 | &init_notify, | ||
174 | &connect_notify, | ||
175 | &disconnect_notify, | ||
176 | handlers); | ||
177 | } | ||
178 | |||
179 | |||
180 | static void | ||
181 | stop_arm (struct PeerContext *p) | ||
182 | { | ||
183 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
184 | "Stopping peer\n"); | ||
185 | if (0 != GNUNET_OS_process_kill (p->arm_proc, | ||
186 | GNUNET_TERM_SIG)) | ||
187 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
188 | "kill"); | ||
189 | if (GNUNET_OK != | ||
190 | GNUNET_OS_process_wait (p->arm_proc)) | ||
191 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
192 | "waitpid"); | ||
193 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
194 | "ARM process %u stopped\n", | ||
195 | (unsigned int) GNUNET_OS_process_get_pid (p->arm_proc)); | ||
196 | GNUNET_OS_process_destroy (p->arm_proc); | ||
197 | p->arm_proc = NULL; | ||
198 | GNUNET_CONFIGURATION_destroy (p->cfg); | ||
199 | } | ||
200 | |||
201 | |||
202 | static int | ||
203 | check () | ||
204 | { | ||
205 | char *const argv[] = { | ||
206 | "test-core-api-start-only", | ||
207 | "-c", | ||
208 | "test_core_api_data.conf", | ||
209 | NULL | ||
210 | }; | ||
211 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
212 | GNUNET_GETOPT_OPTION_END | ||
213 | }; | ||
214 | |||
215 | GNUNET_DISK_purge_cfg_dir | ||
216 | ("test_core_api_peer1.conf", | ||
217 | "GNUNET_TEST_HOME"); | ||
218 | GNUNET_DISK_purge_cfg_dir | ||
219 | ("test_core_api_peer2.conf", | ||
220 | "GNUNET_TEST_HOME"); | ||
221 | |||
222 | ok = 1; | ||
223 | GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1, | ||
224 | argv, | ||
225 | "test-core-api-start-only", | ||
226 | "nohelp", | ||
227 | options, | ||
228 | &run, | ||
229 | &ok); | ||
230 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
231 | "Test finished\n"); | ||
232 | stop_arm (&p1); | ||
233 | stop_arm (&p2); | ||
234 | return ok; | ||
235 | } | ||
236 | |||
237 | |||
238 | int | ||
239 | main (int argc, | ||
240 | char *argv[]) | ||
241 | { | ||
242 | int ret; | ||
243 | |||
244 | GNUNET_log_setup ("test-core-api-start-only", | ||
245 | "WARNING", | ||
246 | NULL); | ||
247 | ret = check (); | ||
248 | GNUNET_DISK_purge_cfg_dir | ||
249 | ("test_core_api_peer1.conf", | ||
250 | "GNUNET_TEST_HOME"); | ||
251 | GNUNET_DISK_purge_cfg_dir | ||
252 | ("test_core_api_peer2.conf", | ||
253 | "GNUNET_TEST_HOME"); | ||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | |||
258 | /* end of test_core_api_start_only.c */ | ||
diff --git a/src/service/core/test_core_defaults.conf b/src/service/core/test_core_defaults.conf new file mode 100644 index 000000000..b098b7d63 --- /dev/null +++ b/src/service/core/test_core_defaults.conf | |||
@@ -0,0 +1,22 @@ | |||
1 | @INLINE@ ../../../contrib/conf/gnunet/no_forcestart.conf | ||
2 | @INLINE@ ../../../contrib/conf/gnunet/no_autostart_above_core.conf | ||
3 | |||
4 | [PATHS] | ||
5 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core/ | ||
6 | |||
7 | [nat] | ||
8 | ENABLE_UPNP = NO | ||
9 | |||
10 | [ats] | ||
11 | WAN_QUOTA_IN = 1 GB | ||
12 | WAN_QUOTA_OUT = 1 GB | ||
13 | |||
14 | [communicator-tcp] | ||
15 | BINDTO = 127.0.0.1 | ||
16 | |||
17 | [communicator-udp] | ||
18 | BROADCAST = NO | ||
19 | |||
20 | [peerinfo] | ||
21 | NO_IO = YES | ||
22 | |||
diff --git a/src/service/core/test_core_just_run.conf b/src/service/core/test_core_just_run.conf new file mode 100644 index 000000000..b7d22f102 --- /dev/null +++ b/src/service/core/test_core_just_run.conf | |||
@@ -0,0 +1,55 @@ | |||
1 | @INLINE@ ../transport/template_tng_cfg_peer1.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ | ||
4 | |||
5 | [transport] | ||
6 | BINARY = gnunet-service-transport | ||
7 | #PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p1.sock | ||
9 | |||
10 | [PEER] | ||
11 | PRIVATE_KEY = $GNUNET_RUNTIME_DIR/private.key | ||
12 | |||
13 | [communicator-tcp] | ||
14 | BINARY = gnunet-communicator-tcp | ||
15 | BINDTO = 192.168.15.1:60002 | ||
16 | DISABLE_V6 = YES | ||
17 | IMMEDIATE_START = YES | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p1.sock | ||
19 | #PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_ctpeer1-%p | ||
20 | #PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args | ||
21 | |||
22 | [communicator-udp] | ||
23 | #PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_cupeer1-%p | ||
24 | BINARY = gnunet-communicator-udp | ||
25 | BINDTO = 192.168.15.1:60002 | ||
26 | DISABLE_V6 = YES | ||
27 | IMMEDIATE_START = YES | ||
28 | UNIXPATH = $GNUNET_RUNTIME_DIR/udp-comm-p1.sock | ||
29 | |||
30 | [peerstore] | ||
31 | IMMEDIATE_START = YES | ||
32 | USE_INCLUDED_HELLOS = YES | ||
33 | PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p | ||
34 | |||
35 | [topology] | ||
36 | IMMEDIATE_START = YES | ||
37 | PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-transport-%p | ||
38 | |||
39 | [dht] | ||
40 | IMMEDIATE_START = YES | ||
41 | PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p | ||
42 | |||
43 | [fs] | ||
44 | IMMEDIATE_START = YES | ||
45 | |||
46 | [hostlist] | ||
47 | IMMEDIATE_START = YES | ||
48 | SERVERS = http://192.168.15.1:8080/ | ||
49 | PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p | ||
50 | |||
51 | [core] | ||
52 | PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-transport-%p | ||
53 | |||
54 | [cadet] | ||
55 | PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-transport-%p | ||
diff --git a/src/service/core/test_core_just_run_host.conf b/src/service/core/test_core_just_run_host.conf new file mode 100644 index 000000000..406b8dcc9 --- /dev/null +++ b/src/service/core/test_core_just_run_host.conf | |||
@@ -0,0 +1,45 @@ | |||
1 | @INLINE@ ../transport/template_tng_cfg_peer1.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ | ||
4 | |||
5 | [transport] | ||
6 | BINARY = gnunet-service-transport | ||
7 | #PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p1.sock | ||
9 | |||
10 | [PEER] | ||
11 | PRIVATE_KEY = $GNUNET_RUNTIME_DIR/private.key | ||
12 | |||
13 | [communicator-tcp] | ||
14 | BINARY = gnunet-communicator-tcp | ||
15 | BINDTO = 192.168.15.1:60002 | ||
16 | DISABLE_V6 = YES | ||
17 | IMMEDIATE_START = YES | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p1.sock | ||
19 | #PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_ctpeer1-%p | ||
20 | #PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args | ||
21 | |||
22 | [communicator-udp] | ||
23 | #PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_cupeer1-%p | ||
24 | BINARY = gnunet-communicator-udp | ||
25 | BINDTO = 192.168.15.1:60002 | ||
26 | DISABLE_V6 = YES | ||
27 | IMMEDIATE_START = YES | ||
28 | UNIXPATH = $GNUNET_RUNTIME_DIR/udp-comm-p1.sock | ||
29 | |||
30 | [peerstore] | ||
31 | IMMEDIATE_START = YES | ||
32 | USE_INCLUDED_HELLOS = YES | ||
33 | |||
34 | [topology] | ||
35 | IMMEDIATE_START = YES | ||
36 | |||
37 | [dht] | ||
38 | IMMEDIATE_START = YES | ||
39 | |||
40 | [fs] | ||
41 | IMMEDIATE_START = YES | ||
42 | |||
43 | [hostlist] | ||
44 | IMMEDIATE_START = YES | ||
45 | OPTIONS = -p \ No newline at end of file | ||
diff --git a/src/service/core/test_core_plugin_cmd_just_run.c b/src/service/core/test_core_plugin_cmd_just_run.c new file mode 100644 index 000000000..3c77c370d --- /dev/null +++ b/src/service/core/test_core_plugin_cmd_just_run.c | |||
@@ -0,0 +1,391 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2021 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file testbed/plugin_cmd_simple_send.c | ||
23 | * @brief a plugin to provide the API for running test cases. | ||
24 | * @author t3sserakt | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_testing_barrier.h" | ||
28 | #include "gnunet_testing_netjail_lib.h" | ||
29 | #include "gnunet_util_lib.h" | ||
30 | #include "gnunet_transport_application_service.h" | ||
31 | #include "gnunet_transport_core_service.h" | ||
32 | #include "gnunet_testing_barrier.h" | ||
33 | #include "gnunet_core_service.h" | ||
34 | #include "gnunet_transport_testing_ng_lib.h" | ||
35 | #include "gnunet_core_testing_lib.h" | ||
36 | |||
37 | /** | ||
38 | * Generic logging shortcut | ||
39 | */ | ||
40 | #define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) | ||
41 | |||
42 | #define BASE_DIR "testdir" | ||
43 | |||
44 | #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) | ||
45 | |||
46 | #define MAX_RECEIVED 1000 | ||
47 | |||
48 | #define MESSAGE_SIZE 65000 | ||
49 | |||
50 | static struct GNUNET_TESTING_Command block_script; | ||
51 | |||
52 | static struct GNUNET_TESTING_Command connect_peers; | ||
53 | |||
54 | static struct GNUNET_TESTING_Command local_prepared; | ||
55 | |||
56 | static struct GNUNET_TESTING_Command start_peer; | ||
57 | |||
58 | static struct GNUNET_TESTING_Interpreter *is; | ||
59 | |||
60 | struct TestState | ||
61 | { | ||
62 | /** | ||
63 | * Callback to write messages to the master loop. | ||
64 | * | ||
65 | */ | ||
66 | GNUNET_TESTING_cmd_helper_write_cb write_message; | ||
67 | |||
68 | /** | ||
69 | * Callback to notify the helper test case has finished. | ||
70 | */ | ||
71 | GNUNET_TESTING_cmd_helper_finish_cb finished_cb; | ||
72 | |||
73 | /** | ||
74 | * The name for a specific test environment directory. | ||
75 | * | ||
76 | */ | ||
77 | char *testdir; | ||
78 | |||
79 | /** | ||
80 | * The name for the configuration file of the specific node. | ||
81 | * | ||
82 | */ | ||
83 | char *cfgname; | ||
84 | |||
85 | /** | ||
86 | * The complete topology information. | ||
87 | */ | ||
88 | struct GNUNET_TESTING_NetjailTopology *topology; | ||
89 | }; | ||
90 | |||
91 | struct Sender | ||
92 | { | ||
93 | /** | ||
94 | * Number of received messages from sender. | ||
95 | */ | ||
96 | unsigned long long num_received; | ||
97 | |||
98 | /** | ||
99 | * Sample mean time the message traveled. | ||
100 | */ | ||
101 | struct GNUNET_TIME_Relative mean_time; | ||
102 | |||
103 | /** | ||
104 | * Time the first message was send. | ||
105 | */ | ||
106 | struct GNUNET_TIME_Absolute time_first; | ||
107 | }; | ||
108 | |||
109 | |||
110 | struct GNUNET_TESTING_BarrierList* | ||
111 | get_waiting_for_barriers () | ||
112 | { | ||
113 | struct GNUNET_TESTING_BarrierList*barriers; | ||
114 | struct GNUNET_TESTING_BarrierListEntry *ble; | ||
115 | |||
116 | barriers = GNUNET_new (struct GNUNET_TESTING_BarrierList); | ||
117 | ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); | ||
118 | ble->barrier_name = "ready-to-connect"; | ||
119 | ble->expected_reaches = 1; | ||
120 | GNUNET_CONTAINER_DLL_insert (barriers->head, | ||
121 | barriers->tail, | ||
122 | ble); | ||
123 | |||
124 | ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); | ||
125 | ble->barrier_name = "test-case-finished"; | ||
126 | ble->expected_reaches = 1; | ||
127 | GNUNET_CONTAINER_DLL_insert (barriers->head, | ||
128 | barriers->tail, | ||
129 | ble); | ||
130 | return barriers; | ||
131 | } | ||
132 | |||
133 | |||
134 | /** | ||
135 | * Function called with the final result of the test. | ||
136 | * | ||
137 | * @param cls the `struct MainParams` | ||
138 | * @param rv #GNUNET_OK if the test passed | ||
139 | */ | ||
140 | static void | ||
141 | handle_result (void *cls, | ||
142 | enum GNUNET_GenericReturnValue rv) | ||
143 | { | ||
144 | struct TestState *ts = cls; | ||
145 | |||
146 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
147 | "Local test exits with status %d\n", | ||
148 | rv); | ||
149 | |||
150 | ts->finished_cb (rv); | ||
151 | GNUNET_free (ts->testdir); | ||
152 | GNUNET_free (ts->cfgname); | ||
153 | GNUNET_TESTING_free_topology (ts->topology); | ||
154 | GNUNET_free (ts); | ||
155 | } | ||
156 | |||
157 | |||
158 | static void | ||
159 | child_completed_callback (void *cls, | ||
160 | enum GNUNET_OS_ProcessStatusType type, | ||
161 | long unsigned int exit_code) | ||
162 | { | ||
163 | |||
164 | } | ||
165 | |||
166 | |||
167 | /** | ||
168 | * Function to start a local test case. | ||
169 | * | ||
170 | * @param write_message Callback to send a message to the master loop. | ||
171 | * @param router_ip Global address of the network namespace. | ||
172 | * @param node_ip The IP address of the node. | ||
173 | * @param m The number of the node in a network namespace. | ||
174 | * @param n The number of the network namespace. | ||
175 | * @param local_m The number of nodes in a network namespace. | ||
176 | * @param topology_data A file name for the file containing the topology configuration, or a string containing | ||
177 | * the topology configuration. | ||
178 | * @param read_file If read_file is GNUNET_YES this string is the filename for the topology configuration, | ||
179 | * if read_file is GNUNET_NO the string contains the topology configuration. | ||
180 | * @param finish_cb Callback function which writes a message from the helper process running on a netjail | ||
181 | * node to the master process * signaling that the test case running on the netjail node finished. | ||
182 | * @return Returns the struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node. | ||
183 | */ | ||
184 | static struct GNUNET_TESTING_Interpreter * | ||
185 | start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, | ||
186 | const char *router_ip, | ||
187 | const char *node_ip, | ||
188 | const char *m, | ||
189 | const char *n, | ||
190 | const char *local_m, | ||
191 | const char *topology_data, | ||
192 | unsigned int *read_file, | ||
193 | GNUNET_TESTING_cmd_helper_finish_cb finished_cb) | ||
194 | { | ||
195 | |||
196 | unsigned int n_int; | ||
197 | unsigned int m_int; | ||
198 | unsigned int local_m_int; | ||
199 | unsigned int num; | ||
200 | struct TestState *ts = GNUNET_new (struct TestState); | ||
201 | struct GNUNET_TESTING_NetjailTopology *topology; | ||
202 | unsigned int sscanf_ret = 0; | ||
203 | char **argv = NULL; | ||
204 | int argc = 0; | ||
205 | |||
206 | ts->finished_cb = finished_cb; | ||
207 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
208 | "n %s m %s\n", | ||
209 | n, | ||
210 | m); | ||
211 | |||
212 | if (GNUNET_YES == *read_file) | ||
213 | { | ||
214 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
215 | "read from file\n"); | ||
216 | topology = GNUNET_TESTING_get_topo_from_file (topology_data); | ||
217 | } | ||
218 | else | ||
219 | topology = GNUNET_TESTING_get_topo_from_string (topology_data); | ||
220 | |||
221 | ts->topology = topology; | ||
222 | |||
223 | errno = 0; | ||
224 | sscanf_ret = sscanf (m, "%u", &m_int); | ||
225 | if (errno != 0) | ||
226 | { | ||
227 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); | ||
228 | } | ||
229 | GNUNET_assert (0 < sscanf_ret); | ||
230 | errno = 0; | ||
231 | sscanf_ret = sscanf (n, "%u", &n_int); | ||
232 | if (errno != 0) | ||
233 | { | ||
234 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); | ||
235 | } | ||
236 | GNUNET_assert (0 < sscanf_ret); | ||
237 | errno = 0; | ||
238 | sscanf_ret = sscanf (local_m, "%u", &local_m_int); | ||
239 | if (errno != 0) | ||
240 | { | ||
241 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); | ||
242 | } | ||
243 | GNUNET_assert (0 < sscanf_ret); | ||
244 | |||
245 | if (0 == n_int) | ||
246 | num = m_int; | ||
247 | else | ||
248 | num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; | ||
249 | |||
250 | block_script = GNUNET_TESTING_cmd_block_until_external_trigger ( | ||
251 | "block-script"); | ||
252 | connect_peers = GNUNET_CORE_cmd_connect_peers ("connect-peers", | ||
253 | "start-peer", | ||
254 | "system-create", | ||
255 | num, | ||
256 | topology, | ||
257 | 0, | ||
258 | GNUNET_NO, | ||
259 | NULL); | ||
260 | local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( | ||
261 | "local-test-prepared", | ||
262 | write_message); | ||
263 | |||
264 | |||
265 | if (1 == m_int) | ||
266 | { | ||
267 | GNUNET_asprintf (&ts->cfgname, | ||
268 | "test_core_just_run_host.conf"); | ||
269 | } | ||
270 | else | ||
271 | { | ||
272 | GNUNET_asprintf (&ts->cfgname, | ||
273 | "test_core_just_run.conf"); | ||
274 | } | ||
275 | |||
276 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
277 | "plugin cfgname: %s\n", | ||
278 | ts->cfgname); | ||
279 | |||
280 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
281 | "node ip: %s\n", | ||
282 | node_ip); | ||
283 | |||
284 | GNUNET_asprintf (&ts->testdir, | ||
285 | "%s%s%s", | ||
286 | BASE_DIR, | ||
287 | m, | ||
288 | n); | ||
289 | |||
290 | /*struct GNUNET_MQ_MessageHandler handlers[] = { | ||
291 | GNUNET_MQ_hd_fixed_size (ephemeral_key, | ||
292 | GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY, | ||
293 | struct EphemeralKeyMessage, | ||
294 | NULL), | ||
295 | GNUNET_MQ_hd_fixed_size (ping, | ||
296 | GNUNET_MESSAGE_TYPE_CORE_PING, | ||
297 | struct PingMessage, | ||
298 | NULL), | ||
299 | GNUNET_MQ_hd_fixed_size (pong, | ||
300 | GNUNET_MESSAGE_TYPE_CORE_PONG, | ||
301 | struct PongMessage, | ||
302 | NULL), | ||
303 | GNUNET_MQ_handler_end () | ||
304 | };*/ | ||
305 | |||
306 | start_peer = GNUNET_TESTING_cmd_start_peer ("start-peer", | ||
307 | "system-create", | ||
308 | num, | ||
309 | node_ip, | ||
310 | ts->cfgname, | ||
311 | GNUNET_NO); | ||
312 | |||
313 | struct GNUNET_TESTING_Command commands[] = { | ||
314 | GNUNET_TESTING_cmd_system_create ("system-create", | ||
315 | ts->testdir), | ||
316 | start_peer, | ||
317 | GNUNET_TESTING_cmd_barrier_reached ("ready-to-connect-reached", | ||
318 | "ready-to-connect", | ||
319 | GNUNET_NO, | ||
320 | num, | ||
321 | GNUNET_NO, | ||
322 | write_message), | ||
323 | connect_peers, | ||
324 | GNUNET_TESTING_cmd_exec_bash_script ("script", | ||
325 | "block.sh", | ||
326 | argv, | ||
327 | argc, | ||
328 | &child_completed_callback), | ||
329 | block_script, | ||
330 | GNUNET_TESTING_cmd_barrier_reached ("test-case-finished-reached", | ||
331 | "test-case-finished", | ||
332 | GNUNET_NO, | ||
333 | num, | ||
334 | GNUNET_NO, | ||
335 | write_message), | ||
336 | GNUNET_TESTING_cmd_stop_peer ("stop-peer", | ||
337 | "start-peer"), | ||
338 | GNUNET_TESTING_cmd_system_destroy ("system-destroy", | ||
339 | "system-create"), | ||
340 | GNUNET_TESTING_cmd_end () | ||
341 | }; | ||
342 | |||
343 | ts->write_message = write_message; | ||
344 | |||
345 | is = GNUNET_TESTING_run (commands, | ||
346 | TIMEOUT, | ||
347 | &handle_result, | ||
348 | ts); | ||
349 | return is; | ||
350 | } | ||
351 | |||
352 | |||
353 | /** | ||
354 | * Entry point for the plugin. | ||
355 | * | ||
356 | * @param cls NULL | ||
357 | * @return the exported block API | ||
358 | */ | ||
359 | void * | ||
360 | libgnunet_test_core_plugin_cmd_just_run_init (void *cls) | ||
361 | { | ||
362 | struct GNUNET_TESTING_PluginFunctions *api; | ||
363 | |||
364 | GNUNET_log_setup ("simple-send", | ||
365 | "DEBUG", | ||
366 | NULL); | ||
367 | |||
368 | api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); | ||
369 | api->start_testcase = &start_testcase; | ||
370 | api->get_waiting_for_barriers = get_waiting_for_barriers; | ||
371 | return api; | ||
372 | } | ||
373 | |||
374 | |||
375 | /** | ||
376 | * Exit point from the plugin. | ||
377 | * | ||
378 | * @param cls the return value from #libgnunet_test_transport_plugin_just_run_init | ||
379 | * @return NULL | ||
380 | */ | ||
381 | void * | ||
382 | libgnunet_test_core_plugin_cmd_just_run_done (void *cls) | ||
383 | { | ||
384 | struct GNUNET_TESTING_PluginFunctions *api = cls; | ||
385 | |||
386 | GNUNET_free (api); | ||
387 | return NULL; | ||
388 | } | ||
389 | |||
390 | |||
391 | /* end of plugin_cmd_simple_send.c */ | ||
diff --git a/src/service/core/test_core_quota_asymmetric_recv_limited_peer1.conf b/src/service/core/test_core_quota_asymmetric_recv_limited_peer1.conf new file mode 100644 index 000000000..766a2e73b --- /dev/null +++ b/src/service/core/test_core_quota_asymmetric_recv_limited_peer1.conf | |||
@@ -0,0 +1,54 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-recv-lim-peer-1/ | ||
4 | |||
5 | [transport-tcp] | ||
6 | PORT = 12488 | ||
7 | |||
8 | [arm] | ||
9 | PORT = 12486 | ||
10 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-arm.sock | ||
11 | |||
12 | [statistics] | ||
13 | PORT = 12487 | ||
14 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-statistics.sock | ||
15 | |||
16 | [resolver] | ||
17 | PORT = 12484 | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-1-service-resolver.sock | ||
19 | |||
20 | [peerinfo] | ||
21 | PORT = 12489 | ||
22 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-peerinfo.sock | ||
23 | |||
24 | [transport] | ||
25 | PORT = 12485 | ||
26 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-transport.sock | ||
27 | |||
28 | [transport-udp] | ||
29 | PORT = 12489 | ||
30 | |||
31 | [ats] | ||
32 | PORT = 12491 | ||
33 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-ats.sock | ||
34 | # UNSPECIFIED | ||
35 | UNSPECIFIED_QUOTA_IN = 100 MiB | ||
36 | UNSPECIFIED_QUOTA_OUT = 100 MiB | ||
37 | # LOOPBACK | ||
38 | LOOPBACK_QUOTA_IN = 100 MiB | ||
39 | LOOPBACK_QUOTA_OUT = 100 MiB | ||
40 | # LAN | ||
41 | LAN_QUOTA_IN = 100 MiB | ||
42 | LAN_QUOTA_OUT = 100 MiB | ||
43 | # WAN | ||
44 | WAN_QUOTA_IN = 100 MiB | ||
45 | WAN_QUOTA_OUT = 100 MiB | ||
46 | # WLAN | ||
47 | WLAN_QUOTA_IN = 100 MiB | ||
48 | WLAN_QUOTA_OUT = 100 MiB | ||
49 | |||
50 | |||
51 | [core] | ||
52 | PORT = 12490 | ||
53 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p1-service-core.sock | ||
54 | |||
diff --git a/src/service/core/test_core_quota_asymmetric_recv_limited_peer2.conf b/src/service/core/test_core_quota_asymmetric_recv_limited_peer2.conf new file mode 100644 index 000000000..30c0bb81f --- /dev/null +++ b/src/service/core/test_core_quota_asymmetric_recv_limited_peer2.conf | |||
@@ -0,0 +1,57 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-recv-lim-peer-2/ | ||
4 | |||
5 | |||
6 | [arm] | ||
7 | PORT = 22486 | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-arm.sock | ||
9 | |||
10 | [statistics] | ||
11 | PORT = 22487 | ||
12 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-statistics.sock | ||
13 | |||
14 | [resolver] | ||
15 | PORT = 22484 | ||
16 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-resolver.sock | ||
17 | |||
18 | [peerinfo] | ||
19 | PORT = 22489 | ||
20 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-peerinfo.sock | ||
21 | |||
22 | [transport] | ||
23 | PORT = 22485 | ||
24 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-transport.sock | ||
25 | |||
26 | [core] | ||
27 | PORT = 22490 | ||
28 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-core.sock | ||
29 | |||
30 | [ats] | ||
31 | PORT = 22491 | ||
32 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-recv-p2-service-ats.sock | ||
33 | # UNSPECIFIED | ||
34 | UNSPECIFIED_QUOTA_IN = 10 MiB | ||
35 | UNSPECIFIED_QUOTA_OUT = 10 MiB | ||
36 | # LOOPBACK | ||
37 | LOOPBACK_QUOTA_IN = 10 MiB | ||
38 | LOOPBACK_QUOTA_OUT = 10 MiB | ||
39 | # LAN | ||
40 | LAN_QUOTA_IN = 10 MiB | ||
41 | LAN_QUOTA_OUT = 10 MiB | ||
42 | # WAN | ||
43 | WAN_QUOTA_IN = 10 MiB | ||
44 | WAN_QUOTA_OUT = 10 MiB | ||
45 | # WLAN | ||
46 | WLAN_QUOTA_IN = 10 MiB | ||
47 | WLAN_QUOTA_OUT = 10 MiB | ||
48 | |||
49 | [transport-tcp] | ||
50 | PORT = 22467 | ||
51 | |||
52 | [transport-http] | ||
53 | PORT = 22469 | ||
54 | |||
55 | [transport-tcp] | ||
56 | PORT = 22468 | ||
57 | |||
diff --git a/src/service/core/test_core_quota_asymmetric_send_limit_peer1.conf b/src/service/core/test_core_quota_asymmetric_send_limit_peer1.conf new file mode 100644 index 000000000..4a9f483d6 --- /dev/null +++ b/src/service/core/test_core_quota_asymmetric_send_limit_peer1.conf | |||
@@ -0,0 +1,52 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-send-lim-peer-1/ | ||
4 | |||
5 | [transport-tcp] | ||
6 | PORT = 12488 | ||
7 | |||
8 | [transport-udp] | ||
9 | PORT = 12492 | ||
10 | |||
11 | [arm] | ||
12 | PORT = 12486 | ||
13 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-arm.sock | ||
14 | |||
15 | [statistics] | ||
16 | PORT = 12487 | ||
17 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-statistics.sock | ||
18 | |||
19 | [resolver] | ||
20 | PORT = 12484 | ||
21 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-1-service-resolver.sock | ||
22 | |||
23 | [peerinfo] | ||
24 | PORT = 12489 | ||
25 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-peerinfo.sock | ||
26 | |||
27 | [transport] | ||
28 | PORT = 12485 | ||
29 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-transport.sock | ||
30 | |||
31 | [ats] | ||
32 | PORT = 12491 | ||
33 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-ats.sock | ||
34 | # UNSPECIFIED | ||
35 | UNSPECIFIED_QUOTA_IN = 10 MiB | ||
36 | UNSPECIFIED_QUOTA_OUT = 10 MiB | ||
37 | # LOOPBACK | ||
38 | LOOPBACK_QUOTA_IN = 10 MiB | ||
39 | LOOPBACK_QUOTA_OUT = 10 MiB | ||
40 | # LAN | ||
41 | LAN_QUOTA_IN = 10 MiB | ||
42 | LAN_QUOTA_OUT = 10 MiB | ||
43 | # WAN | ||
44 | WAN_QUOTA_IN = 10 MiB | ||
45 | WAN_QUOTA_OUT = 10 MiB | ||
46 | # WLAN | ||
47 | WLAN_QUOTA_IN = 10 MiB | ||
48 | WLAN_QUOTA_OUT = 10 MiB | ||
49 | |||
50 | [core] | ||
51 | PORT = 12490 | ||
52 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p1-service-core.sock | ||
diff --git a/src/service/core/test_core_quota_asymmetric_send_limit_peer2.conf b/src/service/core/test_core_quota_asymmetric_send_limit_peer2.conf new file mode 100644 index 000000000..36434461c --- /dev/null +++ b/src/service/core/test_core_quota_asymmetric_send_limit_peer2.conf | |||
@@ -0,0 +1,61 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-quota-asym-send-lim-peer-2/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 22486 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 22487 | ||
11 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-statistics.sock | ||
12 | |||
13 | [resolver] | ||
14 | PORT = 22484 | ||
15 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-resolver.sock | ||
16 | |||
17 | [peerinfo] | ||
18 | PORT = 22489 | ||
19 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-peerinfo.sock | ||
20 | |||
21 | [transport] | ||
22 | PORT = 22485 | ||
23 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-transport.sock | ||
24 | |||
25 | [core] | ||
26 | PORT = 22490 | ||
27 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-core.sock | ||
28 | |||
29 | [ats] | ||
30 | PORT = 22491 | ||
31 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-asym-send-p2-service-ats.sock | ||
32 | WAN_QUOTA_IN = 100 MiB | ||
33 | WAN_QUOTA_OUT = 100 MiB | ||
34 | |||
35 | [ats] | ||
36 | PORT = 12471 | ||
37 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-core-sym-p1-service-ats.sock | ||
38 | # UNSPECIFIED | ||
39 | UNSPECIFIED_QUOTA_IN = 100 MiB | ||
40 | UNSPECIFIED_QUOTA_OUT = 100 MiB | ||
41 | # LOOPBACK | ||
42 | LOOPBACK_QUOTA_IN = 100 MiB | ||
43 | LOOPBACK_QUOTA_OUT = 100 MiB | ||
44 | # LAN | ||
45 | LAN_QUOTA_IN = 100 MiB | ||
46 | LAN_QUOTA_OUT = 100 MiB | ||
47 | # WAN | ||
48 | WAN_QUOTA_IN = 100 MiB | ||
49 | WAN_QUOTA_OUT = 100 MiB | ||
50 | # WLAN | ||
51 | WLAN_QUOTA_IN = 100 MiB | ||
52 | WLAN_QUOTA_OUT = 100 MiB | ||
53 | |||
54 | [transport-tcp] | ||
55 | PORT = 22467 | ||
56 | |||
57 | [transport-udp] | ||
58 | PORT = 22468 | ||
59 | |||
60 | [transport-http] | ||
61 | PORT = 22469 | ||
diff --git a/src/service/core/test_core_quota_compliance.c b/src/service/core/test_core_quota_compliance.c new file mode 100644 index 000000000..099c6fa3b --- /dev/null +++ b/src/service/core/test_core_quota_compliance.c | |||
@@ -0,0 +1,788 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2010, 2015, 2016 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file core/test_core_quota_compliance.c | ||
22 | * @brief testcase for core_api.c focusing quota compliance on core level | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_arm_service.h" | ||
27 | #include "gnunet_core_service.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_ats_service.h" | ||
30 | #include "gnunet_transport_service.h" | ||
31 | #include "gnunet_transport_hello_service.h" | ||
32 | #include "gnunet_statistics_service.h" | ||
33 | |||
34 | |||
35 | #define SYMMETRIC 0 | ||
36 | #define ASYMMETRIC_SEND_LIMITED 1 | ||
37 | #define ASYMMETRIC_RECV_LIMITED 2 | ||
38 | |||
39 | /** | ||
40 | * Note that this value must not significantly exceed | ||
41 | * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise | ||
42 | * messages may be dropped even for a reliable transport. | ||
43 | */ | ||
44 | #define TOTAL_MSGS (60000 * 10) | ||
45 | |||
46 | /** | ||
47 | * How long until we give up on transmitting the message? | ||
48 | */ | ||
49 | #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300) | ||
50 | |||
51 | /** | ||
52 | * What delay do we request from the core service for transmission? | ||
53 | */ | ||
54 | #define FAST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \ | ||
55 | 150) | ||
56 | |||
57 | #define MTYPE 12345 | ||
58 | #define MESSAGESIZE (1024 - 8) | ||
59 | #define MEASUREMENT_LENGTH GNUNET_TIME_relative_multiply ( \ | ||
60 | GNUNET_TIME_UNIT_SECONDS, 30) | ||
61 | |||
62 | static unsigned long long total_bytes_sent; | ||
63 | static unsigned long long total_bytes_recv; | ||
64 | |||
65 | static struct GNUNET_TIME_Absolute start_time; | ||
66 | |||
67 | static struct GNUNET_SCHEDULER_Task *err_task; | ||
68 | |||
69 | static struct GNUNET_SCHEDULER_Task *measure_task; | ||
70 | |||
71 | |||
72 | struct PeerContext | ||
73 | { | ||
74 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
75 | struct GNUNET_CORE_Handle *ch; | ||
76 | struct GNUNET_MQ_Handle *mq; | ||
77 | struct GNUNET_TRANSPORT_OfferHelloHandle *oh; | ||
78 | struct GNUNET_PeerIdentity id; | ||
79 | struct GNUNET_MessageHeader *hello; | ||
80 | struct GNUNET_STATISTICS_Handle *stats; | ||
81 | struct GNUNET_TRANSPORT_HelloGetHandle *ghh; | ||
82 | struct GNUNET_ATS_ConnectivityHandle *ats; | ||
83 | struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh; | ||
84 | int connect_status; | ||
85 | struct GNUNET_OS_Process *arm_proc; | ||
86 | }; | ||
87 | |||
88 | static struct PeerContext p1; | ||
89 | static struct PeerContext p2; | ||
90 | |||
91 | static unsigned long long current_quota_p1_in; | ||
92 | static unsigned long long current_quota_p1_out; | ||
93 | static unsigned long long current_quota_p2_in; | ||
94 | static unsigned long long current_quota_p2_out; | ||
95 | |||
96 | static int ok; | ||
97 | static int test; | ||
98 | static int32_t tr_n; | ||
99 | |||
100 | static int running; | ||
101 | |||
102 | |||
103 | #if VERBOSE | ||
104 | #define OKPP do { ok++; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, \ | ||
105 | "Now at stage %u at %s:%u\n", ok, __FILE__, \ | ||
106 | __LINE__); } while (0) | ||
107 | #else | ||
108 | #define OKPP do { ok++; } while (0) | ||
109 | #endif | ||
110 | |||
111 | struct TestMessage | ||
112 | { | ||
113 | struct GNUNET_MessageHeader header; | ||
114 | uint32_t num GNUNET_PACKED; | ||
115 | uint8_t pad[MESSAGESIZE]; | ||
116 | }; | ||
117 | |||
118 | |||
119 | static void | ||
120 | terminate_peer (struct PeerContext *p) | ||
121 | { | ||
122 | if (NULL != p->ch) | ||
123 | { | ||
124 | GNUNET_CORE_disconnect (p->ch); | ||
125 | p->ch = NULL; | ||
126 | } | ||
127 | if (NULL != p->ghh) | ||
128 | { | ||
129 | GNUNET_TRANSPORT_hello_get_cancel (p->ghh); | ||
130 | p->ghh = NULL; | ||
131 | } | ||
132 | if (NULL != p->oh) | ||
133 | { | ||
134 | GNUNET_TRANSPORT_offer_hello_cancel (p->oh); | ||
135 | p->oh = NULL; | ||
136 | } | ||
137 | if (NULL != p->ats_sh) | ||
138 | { | ||
139 | GNUNET_ATS_connectivity_suggest_cancel (p->ats_sh); | ||
140 | p->ats_sh = NULL; | ||
141 | } | ||
142 | if (NULL != p->ats) | ||
143 | { | ||
144 | GNUNET_ATS_connectivity_done (p->ats); | ||
145 | p->ats = NULL; | ||
146 | } | ||
147 | if (NULL != p->stats) | ||
148 | { | ||
149 | GNUNET_STATISTICS_destroy (p->stats, GNUNET_NO); | ||
150 | p->stats = NULL; | ||
151 | } | ||
152 | if (NULL != p->hello) | ||
153 | { | ||
154 | GNUNET_free (p->hello); | ||
155 | p->hello = NULL; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | |||
160 | static void | ||
161 | shutdown_task (void *cls) | ||
162 | { | ||
163 | if (NULL != err_task) | ||
164 | { | ||
165 | GNUNET_SCHEDULER_cancel (err_task); | ||
166 | err_task = NULL; | ||
167 | } | ||
168 | if (NULL != measure_task) | ||
169 | { | ||
170 | GNUNET_SCHEDULER_cancel (measure_task); | ||
171 | measure_task = NULL; | ||
172 | } | ||
173 | terminate_peer (&p1); | ||
174 | terminate_peer (&p2); | ||
175 | } | ||
176 | |||
177 | |||
178 | static void | ||
179 | terminate_task_error (void *cls) | ||
180 | { | ||
181 | err_task = NULL; | ||
182 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
183 | "Testcase failed (timeout)!\n"); | ||
184 | GNUNET_SCHEDULER_shutdown (); | ||
185 | ok = 42; | ||
186 | } | ||
187 | |||
188 | |||
189 | /** | ||
190 | * Callback function to process statistic values. | ||
191 | * | ||
192 | * @param cls closure | ||
193 | * @param subsystem name of subsystem that created the statistic | ||
194 | * @param name the name of the datum | ||
195 | * @param value the current value | ||
196 | * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not | ||
197 | * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration | ||
198 | */ | ||
199 | static int | ||
200 | print_stat (void *cls, | ||
201 | const char *subsystem, | ||
202 | const char *name, | ||
203 | uint64_t value, | ||
204 | int is_persistent) | ||
205 | { | ||
206 | if (cls == &p1) | ||
207 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
208 | "Peer1 %50s = %12llu\n", | ||
209 | name, | ||
210 | (unsigned long long) value); | ||
211 | if (cls == &p2) | ||
212 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
213 | "Peer2 %50s = %12llu\n", | ||
214 | name, | ||
215 | (unsigned long long) value); | ||
216 | return GNUNET_OK; | ||
217 | } | ||
218 | |||
219 | |||
220 | static void | ||
221 | measurement_stop (void *cls) | ||
222 | { | ||
223 | unsigned long long delta; | ||
224 | unsigned long long throughput_out; | ||
225 | unsigned long long throughput_in; | ||
226 | unsigned long long max_quota_in; | ||
227 | unsigned long long max_quota_out; | ||
228 | unsigned long long quota_delta; | ||
229 | enum GNUNET_ErrorType kind = GNUNET_ERROR_TYPE_DEBUG; | ||
230 | |||
231 | measure_task = NULL; | ||
232 | fprintf (stdout, "%s", "\n"); | ||
233 | running = GNUNET_NO; | ||
234 | |||
235 | delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value_us; | ||
236 | if (0 == delta) | ||
237 | delta = 1; | ||
238 | throughput_out = total_bytes_sent * 1000000LL / delta; /* convert to bytes/s */ | ||
239 | throughput_in = total_bytes_recv * 1000000LL / delta; /* convert to bytes/s */ | ||
240 | |||
241 | max_quota_in = GNUNET_MIN (current_quota_p1_in, current_quota_p2_in); | ||
242 | max_quota_out = GNUNET_MIN (current_quota_p1_out, current_quota_p2_out); | ||
243 | if (max_quota_out < max_quota_in) | ||
244 | quota_delta = max_quota_in / 3; | ||
245 | else | ||
246 | quota_delta = max_quota_out / 3; | ||
247 | |||
248 | if ((throughput_out > (max_quota_out + quota_delta)) || | ||
249 | (throughput_in > (max_quota_in + quota_delta))) | ||
250 | ok = 1; /* fail */ | ||
251 | else | ||
252 | ok = 0; /* pass */ | ||
253 | GNUNET_STATISTICS_get (p1.stats, | ||
254 | "core", | ||
255 | "# discarded CORE_SEND requests", | ||
256 | NULL, | ||
257 | &print_stat, | ||
258 | &p1); | ||
259 | GNUNET_STATISTICS_get (p1.stats, | ||
260 | "core", | ||
261 | "# discarded CORE_SEND request bytes", | ||
262 | NULL, | ||
263 | &print_stat, | ||
264 | &p1); | ||
265 | GNUNET_STATISTICS_get (p1.stats, | ||
266 | "core", | ||
267 | "# discarded lower priority CORE_SEND requests", | ||
268 | NULL, | ||
269 | &print_stat, | ||
270 | NULL); | ||
271 | GNUNET_STATISTICS_get (p1.stats, | ||
272 | "core", | ||
273 | "# discarded lower priority CORE_SEND request bytes", | ||
274 | NULL, | ||
275 | &print_stat, | ||
276 | &p1); | ||
277 | GNUNET_STATISTICS_get (p2.stats, | ||
278 | "core", | ||
279 | "# discarded CORE_SEND requests", | ||
280 | NULL, | ||
281 | &print_stat, | ||
282 | &p2); | ||
283 | |||
284 | GNUNET_STATISTICS_get (p2.stats, | ||
285 | "core", | ||
286 | "# discarded CORE_SEND request bytes", | ||
287 | NULL, | ||
288 | &print_stat, | ||
289 | &p2); | ||
290 | GNUNET_STATISTICS_get (p2.stats, | ||
291 | "core", | ||
292 | "# discarded lower priority CORE_SEND requests", | ||
293 | NULL, | ||
294 | &print_stat, | ||
295 | &p2); | ||
296 | GNUNET_STATISTICS_get (p2.stats, | ||
297 | "core", | ||
298 | "# discarded lower priority CORE_SEND request bytes", | ||
299 | NULL, | ||
300 | &print_stat, | ||
301 | &p2); | ||
302 | |||
303 | if (ok != 0) | ||
304 | kind = GNUNET_ERROR_TYPE_ERROR; | ||
305 | switch (test) | ||
306 | { | ||
307 | case SYMMETRIC: | ||
308 | GNUNET_log (kind, | ||
309 | "Core quota compliance test with symmetric quotas: %s\n", | ||
310 | (0 == ok) ? "PASSED" : "FAILED"); | ||
311 | break; | ||
312 | |||
313 | case ASYMMETRIC_SEND_LIMITED: | ||
314 | GNUNET_log (kind, | ||
315 | "Core quota compliance test with limited sender quota: %s\n", | ||
316 | (0 == ok) ? "PASSED" : "FAILED"); | ||
317 | break; | ||
318 | |||
319 | case ASYMMETRIC_RECV_LIMITED: | ||
320 | GNUNET_log (kind, | ||
321 | "Core quota compliance test with limited receiver quota: %s\n", | ||
322 | (0 == ok) ? "PASSED" : "FAILED"); | ||
323 | break; | ||
324 | } | ||
325 | ; | ||
326 | GNUNET_log (kind, | ||
327 | "Peer 1 send rate: %llu b/s (%llu bytes in %llu ms)\n", | ||
328 | throughput_out, | ||
329 | total_bytes_sent, | ||
330 | delta); | ||
331 | GNUNET_log (kind, | ||
332 | "Peer 1 send quota: %llu b/s\n", | ||
333 | current_quota_p1_out); | ||
334 | GNUNET_log (kind, | ||
335 | "Peer 2 receive rate: %llu b/s (%llu bytes in %llu ms)\n", | ||
336 | throughput_in, | ||
337 | total_bytes_recv, | ||
338 | delta); | ||
339 | GNUNET_log (kind, | ||
340 | "Peer 2 receive quota: %llu b/s\n", | ||
341 | current_quota_p2_in); | ||
342 | /* | ||
343 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Max. inbound quota allowed: %llu b/s\n",max_quota_in ); | ||
344 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Max. outbound quota allowed: %llu b/s\n",max_quota_out); | ||
345 | */ | ||
346 | GNUNET_SCHEDULER_shutdown (); | ||
347 | } | ||
348 | |||
349 | |||
350 | static void | ||
351 | do_transmit (void *cls) | ||
352 | { | ||
353 | struct TestMessage *hdr; | ||
354 | struct GNUNET_MQ_Envelope *env; | ||
355 | |||
356 | env = GNUNET_MQ_msg (hdr, | ||
357 | MTYPE); | ||
358 | hdr->num = htonl (tr_n); | ||
359 | memset (&hdr->pad, | ||
360 | tr_n, | ||
361 | MESSAGESIZE); | ||
362 | tr_n++; | ||
363 | GNUNET_SCHEDULER_cancel (err_task); | ||
364 | err_task = | ||
365 | GNUNET_SCHEDULER_add_delayed (TIMEOUT, | ||
366 | &terminate_task_error, | ||
367 | NULL); | ||
368 | total_bytes_sent += sizeof(struct TestMessage); | ||
369 | GNUNET_MQ_send (p1.mq, | ||
370 | env); | ||
371 | } | ||
372 | |||
373 | |||
374 | static void * | ||
375 | connect_notify (void *cls, | ||
376 | const struct GNUNET_PeerIdentity *peer, | ||
377 | struct GNUNET_MQ_Handle *mq) | ||
378 | { | ||
379 | struct PeerContext *pc = cls; | ||
380 | |||
381 | if (0 == memcmp (&pc->id, | ||
382 | peer, | ||
383 | sizeof(struct GNUNET_PeerIdentity))) | ||
384 | return NULL; /* loopback */ | ||
385 | GNUNET_assert (0 == pc->connect_status); | ||
386 | pc->connect_status = 1; | ||
387 | pc->mq = mq; | ||
388 | if (pc == &p1) | ||
389 | { | ||
390 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
391 | "Encrypted connection established to peer `%s'\n", | ||
392 | GNUNET_i2s (peer)); | ||
393 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
394 | "Asking core (1) for transmission to peer `%s'\n", | ||
395 | GNUNET_i2s (&p2.id)); | ||
396 | GNUNET_SCHEDULER_cancel (err_task); | ||
397 | err_task = | ||
398 | GNUNET_SCHEDULER_add_delayed (TIMEOUT, | ||
399 | &terminate_task_error, | ||
400 | NULL); | ||
401 | start_time = GNUNET_TIME_absolute_get (); | ||
402 | running = GNUNET_YES; | ||
403 | measure_task = | ||
404 | GNUNET_SCHEDULER_add_delayed (MEASUREMENT_LENGTH, | ||
405 | &measurement_stop, | ||
406 | NULL); | ||
407 | do_transmit (NULL); | ||
408 | } | ||
409 | return pc; | ||
410 | } | ||
411 | |||
412 | |||
413 | static void | ||
414 | disconnect_notify (void *cls, | ||
415 | const struct GNUNET_PeerIdentity *peer, | ||
416 | void *internal_cls) | ||
417 | { | ||
418 | struct PeerContext *pc = cls; | ||
419 | |||
420 | if (NULL == internal_cls) | ||
421 | return; /* loopback */ | ||
422 | pc->connect_status = 0; | ||
423 | pc->mq = NULL; | ||
424 | if (NULL != measure_task) | ||
425 | { | ||
426 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
427 | "Measurement aborted due to disconnect!\n"); | ||
428 | GNUNET_SCHEDULER_cancel (measure_task); | ||
429 | measure_task = NULL; | ||
430 | } | ||
431 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
432 | "Encrypted connection to `%s' cut\n", | ||
433 | GNUNET_i2s (peer)); | ||
434 | } | ||
435 | |||
436 | |||
437 | static void | ||
438 | handle_test (void *cls, | ||
439 | const struct TestMessage *hdr) | ||
440 | { | ||
441 | static int n; | ||
442 | |||
443 | total_bytes_recv += sizeof(struct TestMessage); | ||
444 | if (ntohl (hdr->num) != n) | ||
445 | { | ||
446 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
447 | "Expected message %u, got message %u\n", | ||
448 | n, | ||
449 | ntohl (hdr->num)); | ||
450 | GNUNET_SCHEDULER_cancel (err_task); | ||
451 | err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error, | ||
452 | NULL); | ||
453 | return; | ||
454 | } | ||
455 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
456 | "Got message %u\n", | ||
457 | ntohl (hdr->num)); | ||
458 | n++; | ||
459 | if (0 == (n % 10)) | ||
460 | fprintf (stderr, "%s", "."); | ||
461 | |||
462 | if (GNUNET_YES == running) | ||
463 | do_transmit (NULL); | ||
464 | } | ||
465 | |||
466 | |||
467 | static void | ||
468 | init_notify (void *cls, | ||
469 | const struct GNUNET_PeerIdentity *my_identity) | ||
470 | { | ||
471 | struct PeerContext *p = cls; | ||
472 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
473 | GNUNET_MQ_hd_fixed_size (test, | ||
474 | MTYPE, | ||
475 | struct TestMessage, | ||
476 | NULL), | ||
477 | GNUNET_MQ_handler_end () | ||
478 | }; | ||
479 | |||
480 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
481 | "Connection to CORE service of `%s' established\n", | ||
482 | GNUNET_i2s (my_identity)); | ||
483 | GNUNET_assert (NULL != my_identity); | ||
484 | p->id = *my_identity; | ||
485 | if (cls == &p1) | ||
486 | { | ||
487 | GNUNET_assert (ok == 2); | ||
488 | OKPP; | ||
489 | /* connect p2 */ | ||
490 | p2.ch = GNUNET_CORE_connect (p2.cfg, | ||
491 | &p2, | ||
492 | &init_notify, | ||
493 | &connect_notify, | ||
494 | &disconnect_notify, | ||
495 | handlers); | ||
496 | } | ||
497 | else | ||
498 | { | ||
499 | GNUNET_assert (ok == 3); | ||
500 | OKPP; | ||
501 | GNUNET_assert (cls == &p2); | ||
502 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
503 | "Asking core (1) to connect to peer `%s' and vice-versa\n", | ||
504 | GNUNET_i2s (&p2.id)); | ||
505 | p1.ats_sh = GNUNET_ATS_connectivity_suggest (p1.ats, | ||
506 | &p2.id, | ||
507 | 1); | ||
508 | p2.ats_sh = GNUNET_ATS_connectivity_suggest (p2.ats, | ||
509 | &p1.id, | ||
510 | 1); | ||
511 | } | ||
512 | } | ||
513 | |||
514 | |||
515 | static void | ||
516 | offer_hello_done (void *cls) | ||
517 | { | ||
518 | struct PeerContext *p = cls; | ||
519 | |||
520 | p->oh = NULL; | ||
521 | } | ||
522 | |||
523 | |||
524 | static void | ||
525 | process_hello (void *cls, | ||
526 | const struct GNUNET_MessageHeader *message) | ||
527 | { | ||
528 | struct PeerContext *p = cls; | ||
529 | |||
530 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
531 | "Received (my) HELLO from transport service\n"); | ||
532 | GNUNET_assert (message != NULL); | ||
533 | if (NULL != p->hello) GNUNET_free (p->hello); | ||
534 | p->hello = GNUNET_malloc (ntohs (message->size)); | ||
535 | GNUNET_memcpy (p->hello, message, ntohs (message->size)); | ||
536 | if ((p == &p1) && | ||
537 | (NULL == p2.oh)) | ||
538 | p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg, | ||
539 | message, | ||
540 | &offer_hello_done, | ||
541 | &p2); | ||
542 | if ((p == &p2) && | ||
543 | (NULL == p1.oh)) | ||
544 | p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg, message, | ||
545 | &offer_hello_done, | ||
546 | &p1); | ||
547 | |||
548 | if ((p == &p1) && | ||
549 | (NULL != p2.hello) && | ||
550 | (NULL == p1.oh)) | ||
551 | p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg, | ||
552 | p2.hello, | ||
553 | &offer_hello_done, | ||
554 | &p1); | ||
555 | if ((p == &p2) && | ||
556 | (NULL != p1.hello) && | ||
557 | (NULL == p2.oh)) | ||
558 | p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg, | ||
559 | p1.hello, | ||
560 | &offer_hello_done, | ||
561 | &p2); | ||
562 | } | ||
563 | |||
564 | |||
565 | static void | ||
566 | setup_peer (struct PeerContext *p, | ||
567 | const char *cfgname) | ||
568 | { | ||
569 | char *binary; | ||
570 | |||
571 | binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm"); | ||
572 | p->cfg = GNUNET_CONFIGURATION_create (); | ||
573 | p->arm_proc = | ||
574 | GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR | ||
575 | | GNUNET_OS_USE_PIPE_CONTROL, | ||
576 | NULL, NULL, NULL, | ||
577 | binary, | ||
578 | "gnunet-service-arm", | ||
579 | "-c", | ||
580 | cfgname, | ||
581 | NULL); | ||
582 | GNUNET_assert (GNUNET_OK == | ||
583 | GNUNET_CONFIGURATION_load (p->cfg, | ||
584 | cfgname)); | ||
585 | p->stats = GNUNET_STATISTICS_create ("core", | ||
586 | p->cfg); | ||
587 | GNUNET_assert (NULL != p->stats); | ||
588 | p->ats = GNUNET_ATS_connectivity_init (p->cfg); | ||
589 | GNUNET_assert (NULL != p->ats); | ||
590 | p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg, | ||
591 | GNUNET_TRANSPORT_AC_ANY, | ||
592 | &process_hello, | ||
593 | p); | ||
594 | GNUNET_free (binary); | ||
595 | } | ||
596 | |||
597 | |||
598 | static void | ||
599 | run (void *cls, | ||
600 | char *const *args, | ||
601 | const char *cfgfile, | ||
602 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
603 | { | ||
604 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
605 | GNUNET_MQ_hd_fixed_size (test, | ||
606 | MTYPE, | ||
607 | struct TestMessage, | ||
608 | NULL), | ||
609 | GNUNET_MQ_handler_end () | ||
610 | }; | ||
611 | |||
612 | GNUNET_assert (ok == 1); | ||
613 | OKPP; | ||
614 | err_task = | ||
615 | GNUNET_SCHEDULER_add_delayed (TIMEOUT, | ||
616 | &terminate_task_error, | ||
617 | NULL); | ||
618 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
619 | NULL); | ||
620 | if (test == SYMMETRIC) | ||
621 | { | ||
622 | setup_peer (&p1, | ||
623 | "test_core_quota_peer1.conf"); | ||
624 | setup_peer (&p2, | ||
625 | "test_core_quota_peer2.conf"); | ||
626 | } | ||
627 | else if (test == ASYMMETRIC_SEND_LIMITED) | ||
628 | { | ||
629 | setup_peer (&p1, | ||
630 | "test_core_quota_asymmetric_send_limit_peer1.conf"); | ||
631 | setup_peer (&p2, | ||
632 | "test_core_quota_asymmetric_send_limit_peer2.conf"); | ||
633 | } | ||
634 | else if (test == ASYMMETRIC_RECV_LIMITED) | ||
635 | { | ||
636 | setup_peer (&p1, | ||
637 | "test_core_quota_asymmetric_recv_limited_peer1.conf"); | ||
638 | setup_peer (&p2, | ||
639 | "test_core_quota_asymmetric_recv_limited_peer2.conf"); | ||
640 | } | ||
641 | |||
642 | GNUNET_assert (test != -1); | ||
643 | GNUNET_assert (GNUNET_SYSERR != | ||
644 | GNUNET_CONFIGURATION_get_value_size (p1.cfg, | ||
645 | "ATS", | ||
646 | "WAN_QUOTA_IN", | ||
647 | ¤t_quota_p1_in)); | ||
648 | GNUNET_assert (GNUNET_SYSERR != | ||
649 | GNUNET_CONFIGURATION_get_value_size (p2.cfg, | ||
650 | "ATS", | ||
651 | "WAN_QUOTA_IN", | ||
652 | ¤t_quota_p2_in)); | ||
653 | GNUNET_assert (GNUNET_SYSERR != | ||
654 | GNUNET_CONFIGURATION_get_value_size (p1.cfg, | ||
655 | "ATS", | ||
656 | "WAN_QUOTA_OUT", | ||
657 | ¤t_quota_p1_out)); | ||
658 | GNUNET_assert (GNUNET_SYSERR != | ||
659 | GNUNET_CONFIGURATION_get_value_size (p2.cfg, | ||
660 | "ATS", | ||
661 | "WAN_QUOTA_OUT", | ||
662 | ¤t_quota_p2_out)); | ||
663 | |||
664 | p1.ch = GNUNET_CORE_connect (p1.cfg, | ||
665 | &p1, | ||
666 | &init_notify, | ||
667 | &connect_notify, | ||
668 | &disconnect_notify, | ||
669 | handlers); | ||
670 | } | ||
671 | |||
672 | |||
673 | static void | ||
674 | stop_arm (struct PeerContext *p) | ||
675 | { | ||
676 | if (0 != GNUNET_OS_process_kill (p->arm_proc, | ||
677 | GNUNET_TERM_SIG)) | ||
678 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
679 | "kill"); | ||
680 | if (GNUNET_OK != | ||
681 | GNUNET_OS_process_wait (p->arm_proc)) | ||
682 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, | ||
683 | "waitpid"); | ||
684 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
685 | "ARM process %u stopped\n", | ||
686 | GNUNET_OS_process_get_pid (p->arm_proc)); | ||
687 | GNUNET_OS_process_destroy (p->arm_proc); | ||
688 | p->arm_proc = NULL; | ||
689 | GNUNET_CONFIGURATION_destroy (p->cfg); | ||
690 | } | ||
691 | |||
692 | |||
693 | static int | ||
694 | check () | ||
695 | { | ||
696 | char *const argv[] = { | ||
697 | "test-core-quota-compliance", | ||
698 | "-c", | ||
699 | "test_core_api_data.conf", | ||
700 | NULL | ||
701 | }; | ||
702 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
703 | GNUNET_GETOPT_OPTION_END | ||
704 | }; | ||
705 | |||
706 | ok = 1; | ||
707 | GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1, | ||
708 | argv, | ||
709 | "test-core-quota-compliance", | ||
710 | "nohelp", | ||
711 | options, | ||
712 | &run, | ||
713 | &ok); | ||
714 | stop_arm (&p1); | ||
715 | stop_arm (&p2); | ||
716 | return ok; | ||
717 | } | ||
718 | |||
719 | |||
720 | static void | ||
721 | cleanup_directory (int test) | ||
722 | { | ||
723 | switch (test) | ||
724 | { | ||
725 | case SYMMETRIC: | ||
726 | GNUNET_DISK_purge_cfg_dir | ||
727 | ("test_core_quota_peer1.conf", | ||
728 | "GNUNET_TEST_HOME"); | ||
729 | GNUNET_DISK_purge_cfg_dir | ||
730 | ("test_core_quota_peer2.conf", | ||
731 | "GNUNET_TEST_HOME"); | ||
732 | break; | ||
733 | |||
734 | case ASYMMETRIC_SEND_LIMITED: | ||
735 | GNUNET_DISK_purge_cfg_dir | ||
736 | ("test_core_quota_asymmetric_send_limit_peer1.conf", | ||
737 | "GNUNET_TEST_HOME"); | ||
738 | GNUNET_DISK_purge_cfg_dir | ||
739 | ("test_core_quota_asymmetric_send_limit_peer2.conf", | ||
740 | "GNUNET_TEST_HOME"); | ||
741 | break; | ||
742 | |||
743 | case ASYMMETRIC_RECV_LIMITED: | ||
744 | GNUNET_DISK_purge_cfg_dir | ||
745 | ("test_core_quota_asymmetric_recv_limited_peer1.conf", | ||
746 | "GNUNET_TEST_HOME"); | ||
747 | GNUNET_DISK_purge_cfg_dir | ||
748 | ("test_core_quota_asymmetric_recv_limited_peer2.conf", | ||
749 | "GNUNET_TEST_HOME"); | ||
750 | break; | ||
751 | } | ||
752 | } | ||
753 | |||
754 | |||
755 | int | ||
756 | main (int argc, | ||
757 | char *argv[]) | ||
758 | { | ||
759 | int ret; | ||
760 | |||
761 | test = -1; | ||
762 | if (NULL != strstr (argv[0], | ||
763 | "_symmetric")) | ||
764 | { | ||
765 | test = SYMMETRIC; | ||
766 | } | ||
767 | else if (NULL != strstr (argv[0], | ||
768 | "_asymmetric_send")) | ||
769 | { | ||
770 | test = ASYMMETRIC_SEND_LIMITED; | ||
771 | } | ||
772 | else if (NULL != strstr (argv[0], | ||
773 | "_asymmetric_recv")) | ||
774 | { | ||
775 | test = ASYMMETRIC_RECV_LIMITED; | ||
776 | } | ||
777 | GNUNET_assert (test != -1); | ||
778 | cleanup_directory (test); | ||
779 | GNUNET_log_setup ("test-core-quota-compliance", | ||
780 | "WARNING", | ||
781 | NULL); | ||
782 | ret = check (); | ||
783 | cleanup_directory (test); | ||
784 | return ret; | ||
785 | } | ||
786 | |||
787 | |||
788 | /* end of test_core_quota_compliance.c */ | ||
diff --git a/src/service/core/test_core_quota_peer1.conf b/src/service/core/test_core_quota_peer1.conf new file mode 100644 index 000000000..ec592f778 --- /dev/null +++ b/src/service/core/test_core_quota_peer1.conf | |||
@@ -0,0 +1,58 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-1/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 12460 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 12461 | ||
11 | |||
12 | [resolver] | ||
13 | PORT = 12462 | ||
14 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock | ||
15 | |||
16 | [peerinfo] | ||
17 | PORT = 12463 | ||
18 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock | ||
19 | |||
20 | [transport] | ||
21 | PORT = 12464 | ||
22 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock | ||
23 | |||
24 | [core] | ||
25 | PORT = 12475 | ||
26 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-core.sock | ||
27 | |||
28 | [ats] | ||
29 | PORT = 12476 | ||
30 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-ats.sock | ||
31 | # UNSPECIFIED | ||
32 | UNSPECIFIED_QUOTA_IN = 10 MiB | ||
33 | UNSPECIFIED_QUOTA_OUT = 10 MiB | ||
34 | # LOOPBACK | ||
35 | LOOPBACK_QUOTA_IN = 10 MiB | ||
36 | LOOPBACK_QUOTA_OUT = 10 MiB | ||
37 | # LAN | ||
38 | LAN_QUOTA_IN = 10 MiB | ||
39 | LAN_QUOTA_OUT = 10 MiB | ||
40 | # WAN | ||
41 | WAN_QUOTA_IN = 10 MiB | ||
42 | WAN_QUOTA_OUT = 10 MiB | ||
43 | # WLAN | ||
44 | WLAN_QUOTA_IN = 10 MiB | ||
45 | WLAN_QUOTA_OUT = 10 MiB | ||
46 | |||
47 | |||
48 | [transport-tcp] | ||
49 | PORT = 12467 | ||
50 | |||
51 | [transport-udp] | ||
52 | PORT = 12468 | ||
53 | |||
54 | [transport-unix] | ||
55 | PORT = 12469 | ||
56 | |||
57 | [transport-http] | ||
58 | PORT = 12470 | ||
diff --git a/src/service/core/test_core_quota_peer2.conf b/src/service/core/test_core_quota_peer2.conf new file mode 100644 index 000000000..65d0710bb --- /dev/null +++ b/src/service/core/test_core_quota_peer2.conf | |||
@@ -0,0 +1,59 @@ | |||
1 | @INLINE@ test_core_defaults.conf | ||
2 | [PATHS] | ||
3 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-core-peer-2/ | ||
4 | |||
5 | [arm] | ||
6 | PORT = 22460 | ||
7 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock | ||
8 | |||
9 | [statistics] | ||
10 | PORT = 22461 | ||
11 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock | ||
12 | |||
13 | [resolver] | ||
14 | PORT = 22462 | ||
15 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock | ||
16 | |||
17 | [peerinfo] | ||
18 | PORT = 22463 | ||
19 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock | ||
20 | |||
21 | [transport] | ||
22 | PORT = 22464 | ||
23 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock | ||
24 | |||
25 | [core] | ||
26 | PORT = 22475 | ||
27 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-core.sock | ||
28 | |||
29 | [ats] | ||
30 | PORT = 22476 | ||
31 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-ats.sock | ||
32 | # UNSPECIFIED | ||
33 | UNSPECIFIED_QUOTA_IN = 10 MiB | ||
34 | UNSPECIFIED_QUOTA_OUT = 10 MiB | ||
35 | # LOOPBACK | ||
36 | LOOPBACK_QUOTA_IN = 10 MiB | ||
37 | LOOPBACK_QUOTA_OUT = 10 MiB | ||
38 | # LAN | ||
39 | LAN_QUOTA_IN = 10 MiB | ||
40 | LAN_QUOTA_OUT = 10 MiB | ||
41 | # WAN | ||
42 | WAN_QUOTA_IN = 10 MiB | ||
43 | WAN_QUOTA_OUT = 10 MiB | ||
44 | # WLAN | ||
45 | WLAN_QUOTA_IN = 10 MiB | ||
46 | WLAN_QUOTA_OUT = 10 MiB | ||
47 | |||
48 | |||
49 | [transport-tcp] | ||
50 | PORT = 22467 | ||
51 | |||
52 | [transport-udp] | ||
53 | PORT = 22468 | ||
54 | |||
55 | [transport-unix] | ||
56 | PORT = 22469 | ||
57 | |||
58 | [transport-http] | ||
59 | PORT = 22470 | ||
diff --git a/src/service/core/test_core_start_testcase.sh b/src/service/core/test_core_start_testcase.sh new file mode 100755 index 000000000..78e67dbf5 --- /dev/null +++ b/src/service/core/test_core_start_testcase.sh | |||
@@ -0,0 +1,15 @@ | |||
1 | #!/bin/bash | ||
2 | echo gaga1 > gaga.txt | ||
3 | read -p "Test case configuration to use:" conf | ||
4 | if ! [ -d "/run/netns" ]; then | ||
5 | echo You have to create the directory /run/netns. | ||
6 | fi | ||
7 | if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then | ||
8 | if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then | ||
9 | echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" | ||
10 | exit 78 | ||
11 | fi | ||
12 | fi | ||
13 | echo gaga2 >> gaga.txt | ||
14 | exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; /usr/local/lib/gnunet/libexec/test_testing_start_with_config $conf" | ||
15 | echo gaga3 >> gaga.txt | ||