2 This file is part of GNUnet.
3 Copyright (C) 2017 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file cadet/gnunet-service-cadet_core.c
23 * @brief cadet service; interaction with CORE service
24 * @author Bartlomiej Polot
25 * @author Christian Grothoff
27 * All functions in this file should use the prefix GCO (Gnunet Cadet cOre (bottom))
30 * - Optimization: given BROKEN messages, destroy paths (?)
33 #include "gnunet-service-cadet-new_core.h"
34 #include "gnunet-service-cadet-new_paths.h"
35 #include "gnunet-service-cadet-new_peer.h"
36 #include "gnunet-service-cadet-new_connection.h"
37 #include "gnunet-service-cadet-new_tunnels.h"
38 #include "gnunet_core_service.h"
39 #include "cadet_protocol.h"
42 #define LOG(level, ...) GNUNET_log_from(level,"cadet-cor",__VA_ARGS__)
46 * Number of messages we are willing to buffer per route.
48 #define ROUTE_BUFFER_SIZE 8
52 * Information we keep per direction for a route.
59 struct CadetPeer *hop;
62 * Route this direction is part of.
64 struct CadetRoute *my_route;
67 * Message queue manager for @e hop.
69 struct GCP_MessageQueueManager *mqm;
72 * Cyclic message buffer to @e hop.
74 struct GNUNET_MQ_Envelope *out_buffer[ROUTE_BUFFER_SIZE];
77 * Next write offset to use to append messages to @e out_buffer.
79 unsigned int out_wpos;
82 * Next read offset to use to retrieve messages from @e out_buffer.
84 unsigned int out_rpos;
87 * Is @e mqm currently ready for transmission?
95 * Description of a segment of a `struct CadetConnection` at the
96 * intermediate peers. Routes are basically entries in a peer's
97 * routing table for forwarding traffic. At both endpoints, the
98 * routes are terminated by a `struct CadetConnection`, which knows
99 * the complete `struct CadetPath` that is formed by the individual
106 * Information about the next hop on this route.
108 struct RouteDirection next;
111 * Information about the previous hop on this route.
113 struct RouteDirection prev;
116 * Unique identifier for the connection that uses this route.
118 struct GNUNET_CADET_ConnectionTunnelIdentifier cid;
121 * When was this route last in use?
123 struct GNUNET_TIME_Absolute last_use;
129 * Handle to the CORE service.
131 static struct GNUNET_CORE_Handle *core;
134 * Routes on which this peer is an intermediate.
136 static struct GNUNET_CONTAINER_MultiShortmap *routes;
140 * Get the route corresponding to a hash.
142 * @param cid hash generated from the connection identifier
144 static struct CadetRoute *
145 get_route (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid)
147 return GNUNET_CONTAINER_multishortmap_get (routes,
148 &cid->connection_of_tunnel);
153 * We message @a msg from @a prev. Find its route by @a cid and
154 * forward to the next hop. Drop and signal broken route if we do not
157 * @param prev previous hop (sender)
158 * @param cid connection identifier, tells us which route to use
159 * @param msg the message to forward
162 route_message (struct CadetPeer *prev,
163 const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
164 const struct GNUNET_MessageHeader *msg)
166 struct CadetRoute *route;
167 struct RouteDirection *dir;
168 struct GNUNET_MQ_Envelope *env;
170 route = get_route (cid);
173 struct GNUNET_MQ_Envelope *env;
174 struct GNUNET_CADET_ConnectionBrokenMessage *bm;
176 LOG (GNUNET_ERROR_TYPE_DEBUG,
177 "Failed to route message of type %u from %s on connection %s: no route\n",
180 GNUNET_sh2s (&cid->connection_of_tunnel));
181 env = GNUNET_MQ_msg (bm,
182 GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
184 bm->peer1 = my_full_id;
189 dir = (prev == route->prev.hop) ? &route->next : &route->prev;
190 if (GNUNET_YES == dir->is_ready)
192 LOG (GNUNET_ERROR_TYPE_DEBUG,
193 "Routing message of type %u from %s to %s on connection %s\n",
196 GNUNET_i2s (GCP_get_id (dir->hop)),
197 GNUNET_sh2s (&cid->connection_of_tunnel));
198 dir->is_ready = GNUNET_NO;
200 GNUNET_MQ_msg_copy (msg));
203 env = dir->out_buffer[dir->out_wpos];
206 /* Queue full, drop earliest message in queue */
207 LOG (GNUNET_ERROR_TYPE_DEBUG,
208 "Queue full due to new message of type %u from %s to %s on connection %s, dropping old message\n",
211 GNUNET_i2s (GCP_get_id (dir->hop)),
212 GNUNET_sh2s (&cid->connection_of_tunnel));
213 GNUNET_STATISTICS_update (stats,
214 "# messages dropped due to full buffer",
217 GNUNET_assert (dir->out_rpos == dir->out_wpos);
218 GNUNET_MQ_discard (env);
220 if (ROUTE_BUFFER_SIZE == dir->out_rpos)
223 LOG (GNUNET_ERROR_TYPE_DEBUG,
224 "Queueing new message of type %u from %s to %s on connection %s\n",
227 GNUNET_i2s (GCP_get_id (dir->hop)),
228 GNUNET_sh2s (&cid->connection_of_tunnel));
229 env = GNUNET_MQ_msg_copy (msg);
230 dir->out_buffer[dir->out_wpos] = env;
232 if (ROUTE_BUFFER_SIZE == dir->out_wpos)
238 * Check if the create_connection message has the appropriate size.
240 * @param cls Closure (unused).
241 * @param msg Message to check.
243 * @return #GNUNET_YES if size is correct, #GNUNET_NO otherwise.
246 check_connection_create (void *cls,
247 const struct GNUNET_CADET_ConnectionCreateMessage *msg)
249 uint16_t size = ntohs (msg->header.size) - sizeof (*msg);
251 if (0 != (size % sizeof (struct GNUNET_PeerIdentity)))
261 * Free internal data of a route direction.
263 * @param dir direction to destroy (do NOT free memory of 'dir' itself)
266 destroy_direction (struct RouteDirection *dir)
268 for (unsigned int i=0;i<ROUTE_BUFFER_SIZE;i++)
269 if (NULL != dir->out_buffer[i])
271 GNUNET_MQ_discard (dir->out_buffer[i]);
272 dir->out_buffer[i] = NULL;
274 if (NULL != dir->mqm)
276 GCP_request_mq_cancel (dir->mqm,
284 * Destroy our state for @a route.
286 * @param route route to destroy
289 destroy_route (struct CadetRoute *route)
291 LOG (GNUNET_ERROR_TYPE_DEBUG,
292 "Destroying route from %s to %s of connection %s\n",
293 GNUNET_i2s (GCP_get_id (route->prev.hop)),
294 GNUNET_i2s2 (GCP_get_id (route->next.hop)),
295 GNUNET_sh2s (&route->cid.connection_of_tunnel));
296 destroy_direction (&route->prev);
297 destroy_direction (&route->next);
303 * Send message that a route is broken between @a peer1 and @a peer2.
305 * @param target where to send the message
306 * @param cid connection identifier to use
307 * @param peer1 one of the peers where a link is broken
308 * @param peer2 another one of the peers where a link is broken
311 send_broken (struct RouteDirection *target,
312 const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
313 const struct GNUNET_PeerIdentity *peer1,
314 const struct GNUNET_PeerIdentity *peer2)
316 struct GNUNET_MQ_Envelope *env;
317 struct GNUNET_CADET_ConnectionBrokenMessage *bm;
319 if (NULL == target->mqm)
320 return; /* Can't send notification, connection is down! */
321 LOG (GNUNET_ERROR_TYPE_DEBUG,
322 "Notifying %s about BROKEN route at %s-%s of connection %s\n",
323 GCP_2s (target->hop),
326 GNUNET_sh2s (&cid->connection_of_tunnel));
328 env = GNUNET_MQ_msg (bm,
329 GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
336 GCP_request_mq_cancel (target->mqm,
343 * Function called when the message queue to the previous hop
344 * becomes available/unavailable. We expect this function to
345 * be called immediately when we register, and then again
346 * later if the connection ever goes down.
348 * @param cls the `struct RouteDirection`
349 * @param available #GNUNET_YES if sending is now possible,
350 * #GNUNET_NO if sending is no longer possible
351 * #GNUNET_SYSERR if sending is no longer possible
352 * and the last envelope was discarded
355 dir_ready_cb (void *cls,
358 struct RouteDirection *dir = cls;
359 struct CadetRoute *route = dir->my_route;
360 struct RouteDirection *odir;
362 if (GNUNET_YES == ready)
364 struct GNUNET_MQ_Envelope *env;
366 dir->is_ready = GNUNET_YES;
367 if (NULL != (env = dir->out_buffer[dir->out_rpos]))
369 dir->out_buffer[dir->out_rpos] = NULL;
371 if (ROUTE_BUFFER_SIZE == dir->out_rpos)
373 dir->is_ready = GNUNET_NO;
379 odir = (dir == &route->next) ? &route->prev : &route->next;
380 send_broken (&route->next,
382 GCP_get_id (odir->hop),
384 destroy_route (route);
389 * Initialize one of the directions of a route.
391 * @param route route the direction belongs to
392 * @param dir direction to initialize
393 * @param hop next hop on in the @a dir
396 dir_init (struct RouteDirection *dir,
397 struct CadetRoute *route,
398 struct CadetPeer *hop)
401 dir->my_route = route;
402 dir->mqm = GCP_request_mq (hop,
405 GNUNET_assert (GNUNET_YES == dir->is_ready);
410 * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE
412 * @param cls Closure (CadetPeer for neighbor that sent the message).
413 * @param msg Message itself.
416 handle_connection_create (void *cls,
417 const struct GNUNET_CADET_ConnectionCreateMessage *msg)
419 struct CadetPeer *sender = cls;
420 struct CadetPeer *next;
421 const struct GNUNET_PeerIdentity *pids = (const struct GNUNET_PeerIdentity *) &msg[1];
422 struct CadetRoute *route;
423 uint16_t size = ntohs (msg->header.size) - sizeof (*msg);
424 unsigned int path_length;
427 path_length = size / sizeof (struct GNUNET_PeerIdentity);
428 /* Initiator is at offset 0. */
429 for (off=1;off<path_length;off++)
430 if (0 == memcmp (&my_full_id,
432 sizeof (struct GNUNET_PeerIdentity)))
434 if (off == path_length)
436 /* We are not on the path, bogus request */
440 /* Check previous hop */
441 if (sender != GCP_get (&pids[off - 1],
444 /* sender is not on the path, not allowed */
449 get_route (&msg->cid))
451 /* Duplicate CREATE, pass it on, previous one might have been lost! */
452 LOG (GNUNET_ERROR_TYPE_DEBUG,
453 "Passing on duplicate CADET_CONNECTION_CREATE message on connection %s\n",
454 GNUNET_sh2s (&msg->cid.connection_of_tunnel));
455 route_message (sender,
460 if (off == path_length - 1)
462 /* We are the destination, create connection */
463 struct CadetConnection *cc;
464 struct CadetPeerPath *path;
465 struct CadetPeer *origin;
467 cc = GNUNET_CONTAINER_multishortmap_get (connections,
468 &msg->cid.connection_of_tunnel);
471 LOG (GNUNET_ERROR_TYPE_DEBUG,
472 "Received duplicate CADET_CONNECTION_CREATE message on connection %s\n",
473 GNUNET_sh2s (&msg->cid.connection_of_tunnel));
474 GCC_handle_duplicate_create (cc);
478 origin = GCP_get (&pids[0],
480 LOG (GNUNET_ERROR_TYPE_DEBUG,
481 "Received CADET_CONNECTION_CREATE message from %s for connection %s, building inverse path\n",
483 GNUNET_sh2s (&msg->cid.connection_of_tunnel));
484 path = GCPP_get_path_from_route (path_length - 1,
487 GCT_add_inbound_connection (GCP_get_tunnel (origin,
492 /* Send back BROKEN: duplicate connection on the same path,
493 we will use the other one. */
494 struct GNUNET_MQ_Envelope *env;
495 struct GNUNET_CADET_ConnectionBrokenMessage *bm;
497 LOG (GNUNET_ERROR_TYPE_DEBUG,
498 "Received CADET_CONNECTION_CREATE from %s for %s, but %s already has a connection. Sending BROKEN\n",
500 GNUNET_sh2s (&msg->cid.connection_of_tunnel),
502 env = GNUNET_MQ_msg (bm,
503 GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
505 bm->peer1 = my_full_id;
506 GCP_send_ooo (sender,
512 /* We are merely a hop on the way, check if we can support the route */
513 next = GCP_get (&pids[off + 1],
515 if ( (NULL == next) ||
516 (GNUNET_NO == GCP_has_core_connection (next)) )
518 /* unworkable, send back BROKEN notification */
519 struct GNUNET_MQ_Envelope *env;
520 struct GNUNET_CADET_ConnectionBrokenMessage *bm;
522 LOG (GNUNET_ERROR_TYPE_DEBUG,
523 "Received CADET_CONNECTION_CREATE from %s for %s. Next hop %s:%u is down. Sending BROKEN\n",
525 GNUNET_sh2s (&msg->cid.connection_of_tunnel),
526 GNUNET_i2s (&pids[off + 1]),
528 env = GNUNET_MQ_msg (bm,
529 GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
531 bm->peer1 = pids[off + 1];
532 bm->peer2 = my_full_id;
533 GCP_send_ooo (sender,
538 /* Workable route, create routing entry */
539 LOG (GNUNET_ERROR_TYPE_DEBUG,
540 "Received CADET_CONNECTION_CREATE from %s for %s. Next hop %s:%u is up. Creating route\n",
542 GNUNET_sh2s (&msg->cid.connection_of_tunnel),
543 GNUNET_i2s (&pids[off + 1]),
545 route = GNUNET_new (struct CadetRoute);
546 route->cid = msg->cid;
547 dir_init (&route->prev,
550 dir_init (&route->next,
553 GNUNET_assert (GNUNET_OK ==
554 GNUNET_CONTAINER_multishortmap_put (routes,
555 &route->cid.connection_of_tunnel,
557 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
562 * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK
564 * @param cls Closure (CadetPeer for neighbor that sent the message).
565 * @param msg Message itself.
568 handle_connection_create_ack (void *cls,
569 const struct GNUNET_CADET_ConnectionCreateAckMessage *msg)
571 struct CadetPeer *peer = cls;
572 struct CadetConnection *cc;
574 /* First, check if ACK belongs to a connection that ends here. */
575 cc = GNUNET_CONTAINER_multishortmap_get (connections,
576 &msg->cid.connection_of_tunnel);
579 /* verify ACK came from the right direction */
580 struct CadetPeerPath *path = GCC_get_path (cc);
583 GCPP_get_peer_at_offset (path,
586 /* received ACK from unexpected direction, ignore! */
590 LOG (GNUNET_ERROR_TYPE_DEBUG,
591 "Received CONNECTION_CREATE_ACK for connection %s.\n",
592 GNUNET_sh2s (&msg->cid.connection_of_tunnel));
593 GCC_handle_connection_create_ack (cc);
597 /* We're just an intermediary peer, route the message along its path */
605 * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN
607 * @param cls Closure (CadetPeer for neighbor that sent the message).
608 * @param msg Message itself.
609 * @deprecated duplicate logic with #handle_destroy(); dedup!
612 handle_connection_broken (void *cls,
613 const struct GNUNET_CADET_ConnectionBrokenMessage *msg)
615 struct CadetPeer *peer = cls;
616 struct CadetConnection *cc;
617 struct CadetRoute *route;
619 /* First, check if message belongs to a connection that ends here. */
620 cc = GNUNET_CONTAINER_multishortmap_get (connections,
621 &msg->cid.connection_of_tunnel);
624 /* verify message came from the right direction */
625 struct CadetPeerPath *path = GCC_get_path (cc);
628 GCPP_get_peer_at_offset (path,
631 /* received message from unexpected direction, ignore! */
635 LOG (GNUNET_ERROR_TYPE_DEBUG,
636 "Received CONNECTION_BROKEN for connection %s. Destroying it.\n",
637 GNUNET_sh2s (&msg->cid.connection_of_tunnel));
638 GCC_destroy_without_core (cc);
640 /* FIXME: also destroy the path up to the specified link! */
644 /* We're just an intermediary peer, route the message along its path */
645 route = get_route (&msg->cid);
649 destroy_route (route);
650 /* FIXME: also destroy paths we MAY have up to the specified link! */
655 * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY
657 * @param cls Closure (CadetPeer for neighbor that sent the message).
658 * @param msg Message itself.
661 handle_connection_destroy (void *cls,
662 const struct GNUNET_CADET_ConnectionDestroyMessage *msg)
664 struct CadetPeer *peer = cls;
665 struct CadetConnection *cc;
666 struct CadetRoute *route;
668 /* First, check if message belongs to a connection that ends here. */
669 cc = GNUNET_CONTAINER_multishortmap_get (connections,
670 &msg->cid.connection_of_tunnel);
673 /* verify message came from the right direction */
674 struct CadetPeerPath *path = GCC_get_path (cc);
677 GCPP_get_peer_at_offset (path,
680 /* received message from unexpected direction, ignore! */
684 LOG (GNUNET_ERROR_TYPE_DEBUG,
685 "Received CONNECTION_DESTROY for connection %s. Destroying connection.\n",
686 GNUNET_sh2s (&msg->cid.connection_of_tunnel));
688 GCC_destroy_without_core (cc);
692 /* We're just an intermediary peer, route the message along its path */
693 LOG (GNUNET_ERROR_TYPE_DEBUG,
694 "Received CONNECTION_DESTROY for connection %s. Destroying route.\n",
695 GNUNET_sh2s (&msg->cid.connection_of_tunnel));
696 route = get_route (&msg->cid);
700 destroy_route (route);
705 * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX
707 * @param cls Closure (CadetPeer for neighbor that sent the message).
708 * @param msg Message itself.
711 handle_tunnel_kx (void *cls,
712 const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
714 struct CadetPeer *peer = cls;
715 struct CadetConnection *cc;
717 /* First, check if message belongs to a connection that ends here. */
718 cc = GNUNET_CONTAINER_multishortmap_get (connections,
719 &msg->cid.connection_of_tunnel);
722 /* verify message came from the right direction */
723 struct CadetPeerPath *path = GCC_get_path (cc);
726 GCPP_get_peer_at_offset (path,
729 /* received message from unexpected direction, ignore! */
738 /* We're just an intermediary peer, route the message along its path */
746 * Check if the encrypted message has the appropriate size.
748 * @param cls Closure (unused).
749 * @param msg Message to check.
751 * @return #GNUNET_YES if size is correct, #GNUNET_NO otherwise.
754 check_tunnel_encrypted (void *cls,
755 const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
762 * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED.
764 * @param cls Closure (CadetPeer for neighbor that sent the message).
765 * @param msg Message itself.
768 handle_tunnel_encrypted (void *cls,
769 const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
771 struct CadetPeer *peer = cls;
772 struct CadetConnection *cc;
774 /* First, check if message belongs to a connection that ends here. */
775 cc = GNUNET_CONTAINER_multishortmap_get (connections,
776 &msg->cid.connection_of_tunnel);
779 /* verify message came from the right direction */
780 struct CadetPeerPath *path = GCC_get_path (cc);
783 GCPP_get_peer_at_offset (path,
786 /* received message from unexpected direction, ignore! */
790 GCC_handle_encrypted (cc,
794 /* We're just an intermediary peer, route the message along its path */
802 * Function called after #GNUNET_CORE_connect has succeeded (or failed
803 * for good). Note that the private key of the peer is intentionally
804 * not exposed here; if you need it, your process should try to read
805 * the private key file directly (which should work if you are
806 * authorized...). Implementations of this function must not call
807 * #GNUNET_CORE_disconnect (other than by scheduling a new task to
811 * @param my_identity ID of this peer, NULL if we failed
814 core_init_cb (void *cls,
815 const struct GNUNET_PeerIdentity *my_identity)
817 if (NULL == my_identity)
825 sizeof (struct GNUNET_PeerIdentity)));
830 * Method called whenever a given peer connects.
833 * @param peer peer identity this notification is about
836 core_connect_cb (void *cls,
837 const struct GNUNET_PeerIdentity *peer,
838 struct GNUNET_MQ_Handle *mq)
840 struct CadetPeer *cp;
842 LOG (GNUNET_ERROR_TYPE_DEBUG,
843 "CORE connection to peer %s was established.\n",
854 * Method called whenever a peer disconnects.
857 * @param peer peer identity this notification is about
860 core_disconnect_cb (void *cls,
861 const struct GNUNET_PeerIdentity *peer,
864 struct CadetPeer *cp = peer_cls;
866 LOG (GNUNET_ERROR_TYPE_DEBUG,
867 "CORE connection to peer %s went down.\n",
875 * Initialize the CORE subsystem.
877 * @param c Configuration.
880 GCO_init (const struct GNUNET_CONFIGURATION_Handle *c)
882 struct GNUNET_MQ_MessageHandler handlers[] = {
883 GNUNET_MQ_hd_var_size (connection_create,
884 GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE,
885 struct GNUNET_CADET_ConnectionCreateMessage,
887 GNUNET_MQ_hd_fixed_size (connection_create_ack,
888 GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK,
889 struct GNUNET_CADET_ConnectionCreateAckMessage,
891 GNUNET_MQ_hd_fixed_size (connection_broken,
892 GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN,
893 struct GNUNET_CADET_ConnectionBrokenMessage,
895 GNUNET_MQ_hd_fixed_size (connection_destroy,
896 GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY,
897 struct GNUNET_CADET_ConnectionDestroyMessage,
899 GNUNET_MQ_hd_fixed_size (tunnel_kx,
900 GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX,
901 struct GNUNET_CADET_TunnelKeyExchangeMessage,
903 GNUNET_MQ_hd_var_size (tunnel_encrypted,
904 GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED,
905 struct GNUNET_CADET_TunnelEncryptedMessage,
907 GNUNET_MQ_handler_end ()
910 routes = GNUNET_CONTAINER_multishortmap_create (1024,
912 core = GNUNET_CORE_connect (c,
922 * Shut down the CORE subsystem.
929 GNUNET_CORE_disconnect (core);
932 GNUNET_assert (0 == GNUNET_CONTAINER_multishortmap_size (routes));
933 GNUNET_CONTAINER_multishortmap_destroy (routes);
936 /* end of gnunet-cadet-service_core.c */