diff options
author | Christian Grothoff <christian@grothoff.org> | 2017-01-25 18:26:27 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2017-01-25 18:26:27 +0100 |
commit | 3071beacd58c57edba1cb8b392afe6873560c676 (patch) | |
tree | 293171315c0e6328f7f700f847991f399a89fc83 /src/cadet | |
parent | 2882d2f6f37801067190c8c2e9d8d3943362490b (diff) | |
download | gnunet-3071beacd58c57edba1cb8b392afe6873560c676.tar.gz gnunet-3071beacd58c57edba1cb8b392afe6873560c676.zip |
handle ancient/future duplicate payload properly
Diffstat (limited to 'src/cadet')
-rw-r--r-- | src/cadet/gnunet-service-cadet-new_channel.c | 208 |
1 files changed, 135 insertions, 73 deletions
diff --git a/src/cadet/gnunet-service-cadet-new_channel.c b/src/cadet/gnunet-service-cadet-new_channel.c index c4e331304..dc3d4352c 100644 --- a/src/cadet/gnunet-service-cadet-new_channel.c +++ b/src/cadet/gnunet-service-cadet-new_channel.c | |||
@@ -25,8 +25,6 @@ | |||
25 | * @author Christian Grothoff | 25 | * @author Christian Grothoff |
26 | * | 26 | * |
27 | * TODO: | 27 | * TODO: |
28 | * - FIXME: send ACKs back to loopback clients! | ||
29 | * | ||
30 | * - introduce shutdown so we can have half-closed channels, modify | 28 | * - introduce shutdown so we can have half-closed channels, modify |
31 | * destroy to include MID to have FIN-ACK equivalents, etc. | 29 | * destroy to include MID to have FIN-ACK equivalents, etc. |
32 | * - estimate max bandwidth using bursts and use to for CONGESTION CONTROL! | 30 | * - estimate max bandwidth using bursts and use to for CONGESTION CONTROL! |
@@ -59,6 +57,23 @@ | |||
59 | */ | 57 | */ |
60 | #define TIMEOUT_CLOSED_PORT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) | 58 | #define TIMEOUT_CLOSED_PORT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) |
61 | 59 | ||
60 | /** | ||
61 | * How long do we wait at least before retransmitting ever? | ||
62 | */ | ||
63 | #define MIN_RTT_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 75) | ||
64 | |||
65 | /** | ||
66 | * Maximum message ID into the future we accept for out-of-order messages. | ||
67 | * If the message is more than this into the future, we drop it. This is | ||
68 | * important both to detect values that are actually in the past, as well | ||
69 | * as to limit adversarially triggerable memory consumption. | ||
70 | * | ||
71 | * Note that right now we have "max_pending_messages = 4" hard-coded in | ||
72 | * the logic below, so a value of 4 would suffice here. But we plan to | ||
73 | * allow larger windows in the future... | ||
74 | */ | ||
75 | #define MAX_OUT_OF_ORDER_DISTANCE 1024 | ||
76 | |||
62 | 77 | ||
63 | /** | 78 | /** |
64 | * All the states a connection can be in. | 79 | * All the states a connection can be in. |
@@ -1128,8 +1143,9 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch, | |||
1128 | (msg->mid.mid == ch->mid_recv.mid) ) ) | 1143 | (msg->mid.mid == ch->mid_recv.mid) ) ) |
1129 | { | 1144 | { |
1130 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 1145 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
1131 | "Giving %u bytes of payload from %s to client %s\n", | 1146 | "Giving %u bytes of payload with MID %u from %s to client %s\n", |
1132 | (unsigned int) payload_size, | 1147 | (unsigned int) payload_size, |
1148 | ntohl (msg->mid.mid), | ||
1133 | GCCH_2s (ch), | 1149 | GCCH_2s (ch), |
1134 | GSC_2s (ccc->c)); | 1150 | GSC_2s (ccc->c)); |
1135 | ccc->client_ready = GNUNET_NO; | 1151 | ccc->client_ready = GNUNET_NO; |
@@ -1142,9 +1158,28 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch, | |||
1142 | { | 1158 | { |
1143 | struct CadetOutOfOrderMessage *com; | 1159 | struct CadetOutOfOrderMessage *com; |
1144 | int duplicate; | 1160 | int duplicate; |
1145 | 1161 | uint32_t mid_min; | |
1146 | /* FIXME-SECURITY: if the element is WAY too far ahead, | 1162 | uint32_t mid_max; |
1147 | drop it (can't buffer too much!) */ | 1163 | uint32_t mid_msg; |
1164 | |||
1165 | mid_min = ntohl (ch->mid_recv.mid); | ||
1166 | mid_max = mid_min + MAX_OUT_OF_ORDER_DISTANCE; | ||
1167 | mid_msg = ntohl (msg->mid.mid); | ||
1168 | if ( ( (uint32_t) (mid_msg - mid_min) > MAX_OUT_OF_ORDER_DISTANCE) || | ||
1169 | ( (uint32_t) (mid_max - mid_msg) > MAX_OUT_OF_ORDER_DISTANCE) ) | ||
1170 | { | ||
1171 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1172 | "Duplicate ancient or future payload of %u bytes on %s (mid %u) dropped\n", | ||
1173 | (unsigned int) payload_size, | ||
1174 | GCCH_2s (ch), | ||
1175 | ntohl (msg->mid.mid)); | ||
1176 | GNUNET_STATISTICS_update (stats, | ||
1177 | "# duplicate DATA (ancient or future)", | ||
1178 | 1, | ||
1179 | GNUNET_NO); | ||
1180 | GNUNET_MQ_discard (env); | ||
1181 | return; | ||
1182 | } | ||
1148 | 1183 | ||
1149 | com = GNUNET_new (struct CadetOutOfOrderMessage); | 1184 | com = GNUNET_new (struct CadetOutOfOrderMessage); |
1150 | com->mid = msg->mid; | 1185 | com->mid = msg->mid; |
@@ -1188,6 +1223,44 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch, | |||
1188 | 1223 | ||
1189 | 1224 | ||
1190 | /** | 1225 | /** |
1226 | * Function called once the tunnel has sent one of our messages. | ||
1227 | * If the message is unreliable, simply frees the `crm`. If the | ||
1228 | * message was reliable, calculate retransmission time and | ||
1229 | * wait for ACK (or retransmit). | ||
1230 | * | ||
1231 | * @param cls the `struct CadetReliableMessage` that was sent | ||
1232 | */ | ||
1233 | static void | ||
1234 | data_sent_cb (void *cls); | ||
1235 | |||
1236 | |||
1237 | /** | ||
1238 | * We need to retry a transmission, the last one took too long to | ||
1239 | * be acknowledged. | ||
1240 | * | ||
1241 | * @param cls the `struct CadetChannel` where we need to retransmit | ||
1242 | */ | ||
1243 | static void | ||
1244 | retry_transmission (void *cls) | ||
1245 | { | ||
1246 | struct CadetChannel *ch = cls; | ||
1247 | struct CadetReliableMessage *crm = ch->head_sent; | ||
1248 | |||
1249 | ch->retry_data_task = NULL; | ||
1250 | GNUNET_assert (NULL == crm->qe); | ||
1251 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1252 | "Retrying transmission on %s of message %u\n", | ||
1253 | GCCH_2s (ch), | ||
1254 | (unsigned int) ntohl (crm->data_message->mid.mid)); | ||
1255 | crm->qe = GCT_send (ch->t, | ||
1256 | &crm->data_message->header, | ||
1257 | &data_sent_cb, | ||
1258 | crm); | ||
1259 | GNUNET_assert (NULL == ch->retry_data_task); | ||
1260 | } | ||
1261 | |||
1262 | |||
1263 | /** | ||
1191 | * We got an acknowledgement for payload data for a channel. | 1264 | * We got an acknowledgement for payload data for a channel. |
1192 | * Possibly resume transmissions. | 1265 | * Possibly resume transmissions. |
1193 | * | 1266 | * |
@@ -1199,6 +1272,7 @@ GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, | |||
1199 | const struct GNUNET_CADET_ChannelDataAckMessage *ack) | 1272 | const struct GNUNET_CADET_ChannelDataAckMessage *ack) |
1200 | { | 1273 | { |
1201 | struct CadetReliableMessage *crm; | 1274 | struct CadetReliableMessage *crm; |
1275 | int was_head; | ||
1202 | 1276 | ||
1203 | GNUNET_break (GNUNET_NO == ch->is_loopback); | 1277 | GNUNET_break (GNUNET_NO == ch->is_loopback); |
1204 | if (GNUNET_NO == ch->reliable) | 1278 | if (GNUNET_NO == ch->reliable) |
@@ -1225,16 +1299,13 @@ GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, | |||
1225 | GNUNET_NO); | 1299 | GNUNET_NO); |
1226 | return; | 1300 | return; |
1227 | } | 1301 | } |
1302 | was_head = (crm == ch->head_sent); | ||
1228 | GNUNET_CONTAINER_DLL_remove (ch->head_sent, | 1303 | GNUNET_CONTAINER_DLL_remove (ch->head_sent, |
1229 | ch->tail_sent, | 1304 | ch->tail_sent, |
1230 | crm); | 1305 | crm); |
1231 | GNUNET_free (crm->data_message); | 1306 | GNUNET_free (crm->data_message); |
1232 | GNUNET_free (crm); | 1307 | GNUNET_free (crm); |
1233 | ch->pending_messages--; | 1308 | ch->pending_messages--; |
1234 | send_ack_to_client (ch, | ||
1235 | (NULL == ch->owner) | ||
1236 | ? GNUNET_NO | ||
1237 | : GNUNET_YES); | ||
1238 | GNUNET_assert (ch->pending_messages < ch->max_pending_messages); | 1309 | GNUNET_assert (ch->pending_messages < ch->max_pending_messages); |
1239 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 1310 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
1240 | "Received DATA_ACK on %s for message %u (%u ACKs pending)\n", | 1311 | "Received DATA_ACK on %s for message %u (%u ACKs pending)\n", |
@@ -1245,6 +1316,19 @@ GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, | |||
1245 | (NULL == ch->owner) | 1316 | (NULL == ch->owner) |
1246 | ? GNUNET_NO | 1317 | ? GNUNET_NO |
1247 | : GNUNET_YES); | 1318 | : GNUNET_YES); |
1319 | if (was_head) | ||
1320 | { | ||
1321 | if (NULL != ch->retry_data_task) | ||
1322 | { | ||
1323 | GNUNET_SCHEDULER_cancel (ch->retry_data_task); | ||
1324 | ch->retry_data_task = NULL; | ||
1325 | } | ||
1326 | if (NULL != ch->head_sent) | ||
1327 | ch->retry_data_task | ||
1328 | = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry, | ||
1329 | &retry_transmission, | ||
1330 | ch); | ||
1331 | } | ||
1248 | } | 1332 | } |
1249 | 1333 | ||
1250 | 1334 | ||
@@ -1287,39 +1371,24 @@ GCCH_handle_remote_destroy (struct CadetChannel *ch) | |||
1287 | 1371 | ||
1288 | 1372 | ||
1289 | /** | 1373 | /** |
1290 | * Function called once the tunnel has sent one of our messages. | 1374 | * Test if element @a e1 comes before element @a e2. |
1291 | * If the message is unreliable, simply frees the `crm`. If the | ||
1292 | * message was reliable, calculate retransmission time and | ||
1293 | * wait for ACK (or retransmit). | ||
1294 | * | ||
1295 | * @param cls the `struct CadetReliableMessage` that was sent | ||
1296 | */ | ||
1297 | static void | ||
1298 | data_sent_cb (void *cls); | ||
1299 | |||
1300 | |||
1301 | /** | ||
1302 | * We need to retry a transmission, the last one took too long to | ||
1303 | * be acknowledged. | ||
1304 | * | 1375 | * |
1305 | * @param cls the `struct CadetChannel` where we need to retransmit | 1376 | * @param cls closure, to a flag where we indicate duplicate packets |
1377 | * @param crm1 an element of to sort | ||
1378 | * @param crm2 another element to sort | ||
1379 | * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO | ||
1306 | */ | 1380 | */ |
1307 | static void | 1381 | static int |
1308 | retry_transmission (void *cls) | 1382 | cmp_crm_by_next_retry (void *cls, |
1383 | struct CadetReliableMessage *crm1, | ||
1384 | struct CadetReliableMessage *crm2) | ||
1309 | { | 1385 | { |
1310 | struct CadetChannel *ch = cls; | 1386 | if (crm1->next_retry.abs_value_us < |
1311 | struct CadetReliableMessage *crm = ch->head_sent; | 1387 | crm2->next_retry.abs_value_us) |
1312 | 1388 | return GNUNET_YES; | |
1313 | ch->retry_data_task = NULL; | 1389 | return GNUNET_NO; |
1314 | GNUNET_assert (NULL == crm->qe); | ||
1315 | crm->qe = GCT_send (ch->t, | ||
1316 | &crm->data_message->header, | ||
1317 | &data_sent_cb, | ||
1318 | crm); | ||
1319 | GNUNET_assert (NULL == ch->retry_data_task); | ||
1320 | } | 1390 | } |
1321 | 1391 | ||
1322 | |||
1323 | /** | 1392 | /** |
1324 | * Function called once the tunnel has sent one of our messages. | 1393 | * Function called once the tunnel has sent one of our messages. |
1325 | * If the message is unreliable, simply frees the `crm`. If the | 1394 | * If the message is unreliable, simply frees the `crm`. If the |
@@ -1333,12 +1402,10 @@ data_sent_cb (void *cls) | |||
1333 | { | 1402 | { |
1334 | struct CadetReliableMessage *crm = cls; | 1403 | struct CadetReliableMessage *crm = cls; |
1335 | struct CadetChannel *ch = crm->ch; | 1404 | struct CadetChannel *ch = crm->ch; |
1336 | struct CadetReliableMessage *off; | ||
1337 | 1405 | ||
1338 | GNUNET_assert (GNUNET_NO == ch->is_loopback); | 1406 | GNUNET_assert (GNUNET_NO == ch->is_loopback); |
1339 | GNUNET_assert (NULL != crm->qe); | 1407 | GNUNET_assert (NULL != crm->qe); |
1340 | crm->qe = NULL; | 1408 | crm->qe = NULL; |
1341 | GNUNET_assert (NULL == ch->retry_data_task); | ||
1342 | GNUNET_CONTAINER_DLL_remove (ch->head_sent, | 1409 | GNUNET_CONTAINER_DLL_remove (ch->head_sent, |
1343 | ch->tail_sent, | 1410 | ch->tail_sent, |
1344 | crm); | 1411 | crm); |
@@ -1355,40 +1422,33 @@ data_sent_cb (void *cls) | |||
1355 | } | 1422 | } |
1356 | if (0 == crm->retry_delay.rel_value_us) | 1423 | if (0 == crm->retry_delay.rel_value_us) |
1357 | crm->retry_delay = ch->expected_delay; | 1424 | crm->retry_delay = ch->expected_delay; |
1425 | else | ||
1426 | crm->retry_delay = GNUNET_TIME_STD_BACKOFF (crm->retry_delay); | ||
1427 | crm->retry_delay = GNUNET_TIME_relative_max (crm->retry_delay, | ||
1428 | MIN_RTT_DELAY); | ||
1358 | crm->next_retry = GNUNET_TIME_relative_to_absolute (crm->retry_delay); | 1429 | crm->next_retry = GNUNET_TIME_relative_to_absolute (crm->retry_delay); |
1359 | 1430 | ||
1360 | /* find position for re-insertion into the DLL */ | 1431 | GNUNET_CONTAINER_DLL_insert_sorted (struct CadetReliableMessage, |
1361 | if ( (NULL == ch->head_sent) || | 1432 | cmp_crm_by_next_retry, |
1362 | (crm->next_retry.abs_value_us < ch->head_sent->next_retry.abs_value_us) ) | 1433 | NULL, |
1363 | { | 1434 | ch->head_sent, |
1364 | /* insert at HEAD, also (re)schedule retry task! */ | ||
1365 | GNUNET_CONTAINER_DLL_insert (ch->head_sent, | ||
1366 | ch->tail_sent, | ||
1367 | crm); | ||
1368 | GNUNET_assert (NULL == crm->qe); | ||
1369 | ch->retry_data_task | ||
1370 | = GNUNET_SCHEDULER_add_delayed (crm->retry_delay, | ||
1371 | &retry_transmission, | ||
1372 | ch); | ||
1373 | return; | ||
1374 | } | ||
1375 | for (off = ch->head_sent; NULL != off; off = off->next) | ||
1376 | if (crm->next_retry.abs_value_us < off->next_retry.abs_value_us) | ||
1377 | break; | ||
1378 | if (NULL == off) | ||
1379 | { | ||
1380 | /* insert at tail */ | ||
1381 | GNUNET_CONTAINER_DLL_insert_tail (ch->head_sent, | ||
1382 | ch->tail_sent, | 1435 | ch->tail_sent, |
1383 | crm); | 1436 | crm); |
1384 | } | 1437 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
1385 | else | 1438 | "Message %u sent, next transmission on %s in %s\n", |
1439 | (unsigned int) ntohl (crm->data_message->mid.mid), | ||
1440 | GCCH_2s (ch), | ||
1441 | GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (ch->head_sent->next_retry), | ||
1442 | GNUNET_YES)); | ||
1443 | if (crm == ch->head_sent) | ||
1386 | { | 1444 | { |
1387 | /* insert before off */ | 1445 | /* We are the new head, need to reschedule retry task */ |
1388 | GNUNET_CONTAINER_DLL_insert_after (ch->head_sent, | 1446 | if (NULL != ch->retry_data_task) |
1389 | ch->tail_sent, | 1447 | GNUNET_SCHEDULER_cancel (ch->retry_data_task); |
1390 | off->prev, | 1448 | ch->retry_data_task |
1391 | crm); | 1449 | = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry, |
1450 | &retry_transmission, | ||
1451 | ch); | ||
1392 | } | 1452 | } |
1393 | } | 1453 | } |
1394 | 1454 | ||
@@ -1486,9 +1546,10 @@ GCCH_handle_local_data (struct CadetChannel *ch, | |||
1486 | ch->tail_sent, | 1546 | ch->tail_sent, |
1487 | crm); | 1547 | crm); |
1488 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 1548 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
1489 | "Sending %u bytes from local client to %s\n", | 1549 | "Sending %u bytes from local client to %s with MID %u\n", |
1490 | buf_len, | 1550 | buf_len, |
1491 | GCCH_2s (ch)); | 1551 | GCCH_2s (ch), |
1552 | ntohl (crm->data_message->mid.mid)); | ||
1492 | if (NULL != ch->retry_data_task) | 1553 | if (NULL != ch->retry_data_task) |
1493 | { | 1554 | { |
1494 | GNUNET_SCHEDULER_cancel (ch->retry_data_task); | 1555 | GNUNET_SCHEDULER_cancel (ch->retry_data_task); |
@@ -1530,9 +1591,10 @@ GCCH_handle_local_ack (struct CadetChannel *ch, | |||
1530 | if (NULL == com) | 1591 | if (NULL == com) |
1531 | { | 1592 | { |
1532 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 1593 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
1533 | "Got LOCAL_ACK, %s-%X ready to receive more data (but none pending)!\n", | 1594 | "Got LOCAL_ACK, %s-%X ready to receive more data (but none pending on %s)!\n", |
1534 | GSC_2s (ccc->c), | 1595 | GSC_2s (ccc->c), |
1535 | ntohl (ccc->ccn.channel_of_client)); | 1596 | ntohl (ccc->ccn.channel_of_client), |
1597 | GCCH_2s (ch)); | ||
1536 | return; /* none pending */ | 1598 | return; /* none pending */ |
1537 | } | 1599 | } |
1538 | if (GNUNET_YES == ch->is_loopback) | 1600 | if (GNUNET_YES == ch->is_loopback) |