+ * Core callback to write a pre-constructed data packet to core buffer
+ *
+ * @param cls Closure (MeshTransmissionDescriptor with data in "data" member).
+ * @param size Number of bytes available in buf.
+ * @param buf Where the to write the message.
+ *
+ * @return number of bytes written to buf
+ */
+static size_t
+send_core_data_raw (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_MessageHeader *msg = cls;
+ size_t total_size;
+
+ GNUNET_assert (NULL != msg);
+ total_size = ntohs (msg->size);
+
+ if (total_size > size)
+ {
+ GNUNET_break (0);
+ return 0;
+ }
+ memcpy (buf, msg, total_size);
+ GNUNET_free (cls);
+ return total_size;
+}
+
+
+/**
+ * Function to send a create path packet to a peer.
+ *
+ * @param cls closure
+ * @param size number of bytes available in buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf
+ */
+static size_t
+send_core_path_create (void *cls, size_t size, void *buf)
+{
+ struct MeshTunnel *t = cls;
+ struct GNUNET_MESH_CreateTunnel *msg;
+ struct GNUNET_PeerIdentity *peer_ptr;
+ struct MeshPeerPath *p = t->path;
+ size_t size_needed;
+ uint32_t opt;
+ int i;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CREATE PATH sending...\n");
+ size_needed =
+ sizeof (struct GNUNET_MESH_CreateTunnel) +
+ p->length * sizeof (struct GNUNET_PeerIdentity);
+
+ if (size < size_needed || NULL == buf)
+ {
+ GNUNET_break (0);
+ return 0;
+ }
+ msg = (struct GNUNET_MESH_CreateTunnel *) buf;
+ msg->header.size = htons (size_needed);
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE);
+ msg->tid = ntohl (t->id.tid);
+
+ opt = 0;
+ if (GNUNET_YES == t->nobuffer)
+ opt |= GNUNET_MESH_OPTION_NOBUFFER;
+ if (GNUNET_YES == t->reliable)
+ opt |= GNUNET_MESH_OPTION_RELIABLE;
+ msg->opt = htonl (opt);
+ msg->port = htonl (t->port);
+
+ peer_ptr = (struct GNUNET_PeerIdentity *) &msg[1];
+ for (i = 0; i < p->length; i++)
+ {
+ GNUNET_PEER_resolve (p->peers[i], peer_ptr++);
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "CREATE PATH (%u bytes long) sent!\n", size_needed);
+ return size_needed;
+}
+
+
+/**
+ * Creates a path ack message in buf and frees all unused resources.
+ *
+ * @param cls closure (MeshTransmissionDescriptor)
+ * @param size number of bytes available in buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf
+ */
+static size_t
+send_core_path_ack (void *cls, size_t size, void *buf)
+{
+ struct MeshTunnel *t = cls;
+ struct GNUNET_MESH_PathACK *msg = buf;
+
+ GNUNET_assert (NULL != t);
+ if (sizeof (struct GNUNET_MESH_PathACK) > size)
+ {
+ GNUNET_break (0);
+ return 0;
+ }
+ t->prev_fc.last_ack_sent = t->nobuffer ? 0 : t->queue_max - 1;
+ msg->header.size = htons (sizeof (struct GNUNET_MESH_PathACK));
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_ACK);
+ GNUNET_PEER_resolve (t->id.oid, &msg->oid);
+ msg->tid = htonl (t->id.tid);
+ msg->peer_id = my_full_id;
+ msg->ack = htonl (t->prev_fc.last_ack_sent);
+
+ /* TODO add signature */
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "PATH ACK sent!\n");
+ return sizeof (struct GNUNET_MESH_PathACK);
+}
+
+
+/**
+ * Iterator over all the peers to remove the oldest not-used entry.
+ *
+ * @param cls Closure (unsued).
+ * @param key ID of the peer.
+ * @param value Peer_Info of the peer.
+ *
+ * FIXME implement
+ */
+static int
+peer_timeout (void *cls,
+ const struct GNUNET_PeerIdentity *key,
+ void *value)
+{
+ return GNUNET_YES;
+}
+
+
+/**
+ * Retrieve the MeshPeer stucture associated with the peer, create one
+ * and insert it in the appropriate structures if the peer is not known yet.
+ *
+ * @param peer Full identity of the peer.
+ *
+ * @return Existing or newly created peer info.
+ */
+static struct MeshPeer *
+peer_get (const struct GNUNET_PeerIdentity *peer_id)
+{
+ struct MeshPeer *peer;
+
+ peer = GNUNET_CONTAINER_multipeermap_get (peers, peer_id);
+ if (NULL == peer)
+ {
+ peer = GNUNET_new (struct MeshPeer);
+ if (GNUNET_CONTAINER_multipeermap_size (peers) > max_peers)
+ {
+ GNUNET_CONTAINER_multipeermap_iterate (peers,
+ &peer_timeout,
+ NULL);
+ }
+ GNUNET_CONTAINER_multipeermap_put (peers, peer_id, peer,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+ peer->id = GNUNET_PEER_intern (peer_id);
+ }
+ peer->last_contact = GNUNET_TIME_absolute_get();
+
+ return peer;
+}
+
+
+/**
+ * Retrieve the MeshPeer stucture associated with the peer, create one
+ * and insert it in the appropriate structures if the peer is not known yet.
+ *
+ * @param peer Short identity of the peer.
+ *
+ * @return Existing or newly created peer info.
+ */
+static struct MeshPeer *
+peer_get_short (const GNUNET_PEER_Id peer)
+{
+ return peer_get (GNUNET_PEER_resolve2 (peer));
+}
+
+
+/**
+ * Select which PID to POLL for, to compensate for lost messages.
+ *
+ * @param pi Peer we want to poll.
+ * @param t Tunnel about which we want to poll.
+ *
+ * @return PID to use, either last sent or first_in_queue - 1
+ */
+static uint32_t
+peer_get_first_payload_pid (struct MeshPeer *p, struct MeshTunnel *t)
+{
+ struct MeshPeerQueue *q;
+ uint16_t type;
+
+ type = p->id == t->next_hop ? GNUNET_MESSAGE_TYPE_MESH_UNICAST :
+ GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN;
+
+ for (q = p->queue_head; NULL != q; q = q->next)
+ {
+ if (q->type == type && q->tunnel == t)
+ {
+ struct GNUNET_MESH_Data *msg = q->cls;
+
+ /* Pretend that the last one sent was the previous to this */
+ return ntohl (msg->pid) - 1;
+ }
+ }
+
+ /* No data in queue, use last sent */
+ {
+ struct MeshFlowControl *fc;
+
+ fc = p->id == t->next_hop ? &t->next_fc : &t->prev_fc;
+ return fc->last_pid_sent;
+ }
+}
+
+
+/**
+ * Choose the best path towards a peer considering the tunnel properties.
+ *
+ * @param peer The destination peer.
+ * @param t The tunnel the path is for.
+ *
+ * @return Best current known path towards the peer, if any.
+ */
+static struct MeshPeerPath *
+peer_get_best_path (const struct MeshPeer *peer, const struct MeshTunnel *t)
+{
+ struct MeshPeerPath *best_p;
+ struct MeshPeerPath *p;
+ unsigned int best_cost;
+ unsigned int cost;
+
+ best_p = p = peer->path_head;
+ best_cost = cost = p->length;
+ while (NULL != p)
+ {
+ if ((cost = p->length) < best_cost)
+ {
+ best_cost = cost;
+ best_p = p;
+ }
+ p = p->next;
+ }
+ return best_p;
+}
+
+
+/**
+ * Try to establish a new connection to this peer in the given tunnel.
+ * If the peer doesn't have any path to it yet, try to get one.
+ * If the peer already has some path, send a CREATE PATH towards it.
+ *
+ * @param peer PeerInfo of the peer.
+ * @param t Tunnel for which to create the path, if possible.
+ */
+static void
+peer_connect (struct MeshPeer *peer, struct MeshTunnel *t)
+{
+ struct MeshPeerPath *p;
+ struct GNUNET_HashCode phash;
+
+ if (NULL != peer->path_head)
+ {
+ p = peer_get_best_path (peer, t);
+ tunnel_use_path (t, p);
+ send_path_create (t);
+ }
+ else if (NULL == peer->dhtget)
+ {
+ struct GNUNET_PeerIdentity id;
+
+ GNUNET_PEER_resolve (peer->id, &id);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Starting DHT GET for peer %s\n",
+ GNUNET_i2s (&id));
+ GNUNET_CRYPTO_hash (&id, sizeof (my_full_id), &phash);
+ peer->dhtget = GNUNET_DHT_get_start (dht_handle, /* handle */
+ GNUNET_BLOCK_TYPE_MESH_PEER, /* type */
+ &phash, /* key to search */
+ dht_replication_level, /* replication level */
+ GNUNET_DHT_RO_RECORD_ROUTE |
+ GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
+ NULL, /* xquery */
+ 0, /* xquery bits */
+ &dht_get_id_handler, peer);
+ tunnel_change_state (t, MESH_TUNNEL_SEARCHING);
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "There is no path but the DHT GET is already started.\n");
+ }
+}
+
+
+/**
+ * @brief Re-initiate traffic to this peer if necessary.
+ *
+ * Check if there is traffic queued towards this peer
+ * and the core transmit handle is NULL (traffic was stalled).
+ * If so, call core tmt rdy.
+ *
+ * @param peer_id Short ID of peer to which initiate traffic.
+ */
+static void
+peer_unlock_queue (GNUNET_PEER_Id peer_id)
+{
+ struct MeshPeer *peer;
+ struct MeshPeerQueue *q;
+ size_t size;
+
+ peer = peer_get_short (peer_id);
+ if (NULL != peer->core_transmit)
+ return;
+
+ q = queue_get_next (peer);
+ if (NULL == q)
+ return;
+
+ size = q->size;
+ peer->core_transmit =
+ GNUNET_CORE_notify_transmit_ready (core_handle,
+ GNUNET_NO,
+ 0,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_PEER_resolve2 (peer->id),
+ size,
+ &queue_send,
+ peer);
+}
+
+
+/**
+ * Cancel all transmissions towards a neighbor that belong to a certain tunnel.
+ *
+ * @param neighbor Short ID of the neighbor to whom cancel the transmissions.
+ * @param t Tunnel which to cancel.
+ */
+static void
+peer_cancel_queues (GNUNET_PEER_Id neighbor, struct MeshTunnel *t)
+{
+ struct MeshPeer *peer;
+ struct MeshPeerQueue *q;
+ struct MeshPeerQueue *next;
+ struct MeshFlowControl *fc;
+
+ if (0 == neighbor)
+ return; /* Was local peer, 0'ed in tunnel_destroy_iterator */
+ peer = peer_get_short (neighbor);
+ for (q = peer->queue_head; NULL != q; q = next)
+ {
+ next = q->next;
+ if (q->tunnel == t)
+ {
+ if (GNUNET_MESSAGE_TYPE_MESH_UNICAST == q->type ||
+ GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN == q->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "peer_cancel_queues %s\n",
+ GNUNET_MESH_DEBUG_M2S (q->type));
+ }
+ queue_destroy (q, GNUNET_YES);
+ }
+ }
+ if (NULL == peer->queue_head && NULL != peer->core_transmit)
+ {
+ GNUNET_CORE_notify_transmit_ready_cancel (peer->core_transmit);
+ peer->core_transmit = NULL;
+ }
+ fc = neighbor == t->next_hop ? &t->next_fc : &t->prev_fc;
+ if (GNUNET_SCHEDULER_NO_TASK != fc->poll_task)
+ {
+ GNUNET_SCHEDULER_cancel (fc->poll_task);
+ fc->poll_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+}