diff options
author | Christian Grothoff <christian@grothoff.org> | 2015-02-03 13:14:07 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2015-02-03 13:14:07 +0000 |
commit | 764e5806e7b905d7c2babef0eced934cc6c48f1e (patch) | |
tree | b6c52889c20f9fd2ce6fe46b2e69a15a64109c2e /src/transport/gnunet-service-transport_neighbours.c | |
parent | bee9a02b062e1869f2a0f6f6e6b0739f7cb2174d (diff) | |
download | gnunet-764e5806e7b905d7c2babef0eced934cc6c48f1e.tar.gz gnunet-764e5806e7b905d7c2babef0eced934cc6c48f1e.zip |
when ATS suggests an address that neighbours does not have a peer for, do not ignore it, setup the neighbour record instead
Diffstat (limited to 'src/transport/gnunet-service-transport_neighbours.c')
-rw-r--r-- | src/transport/gnunet-service-transport_neighbours.c | 241 |
1 files changed, 118 insertions, 123 deletions
diff --git a/src/transport/gnunet-service-transport_neighbours.c b/src/transport/gnunet-service-transport_neighbours.c index 7df23c2c2..1ecf71517 100644 --- a/src/transport/gnunet-service-transport_neighbours.c +++ b/src/transport/gnunet-service-transport_neighbours.c | |||
@@ -2178,7 +2178,6 @@ try_connect_bl_check_cont (void *cls, | |||
2178 | } | 2178 | } |
2179 | 2179 | ||
2180 | 2180 | ||
2181 | |||
2182 | /** | 2181 | /** |
2183 | * Try to create a connection to the given target (eventually). | 2182 | * Try to create a connection to the given target (eventually). |
2184 | * | 2183 | * |
@@ -2248,10 +2247,15 @@ GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target) | |||
2248 | 2247 | ||
2249 | /* Do blacklist check if connecting to this peer is allowed */ | 2248 | /* Do blacklist check if connecting to this peer is allowed */ |
2250 | blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext); | 2249 | blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext); |
2251 | GNUNET_CONTAINER_DLL_insert (pending_bc_head, pending_bc_tail, blc_ctx); | 2250 | GNUNET_CONTAINER_DLL_insert (pending_bc_head, |
2251 | pending_bc_tail, | ||
2252 | blc_ctx); | ||
2252 | 2253 | ||
2253 | if (NULL != (blc = GST_blacklist_test_allowed (target, NULL, | 2254 | if (NULL != |
2254 | &try_connect_bl_check_cont, blc_ctx))) | 2255 | (blc = GST_blacklist_test_allowed (target, |
2256 | NULL, | ||
2257 | &try_connect_bl_check_cont, | ||
2258 | blc_ctx))) | ||
2255 | { | 2259 | { |
2256 | blc_ctx->blc = blc; | 2260 | blc_ctx->blc = blc; |
2257 | } | 2261 | } |
@@ -2391,9 +2395,66 @@ GST_neighbours_handle_session_syn (const struct GNUNET_MessageHeader *message, | |||
2391 | 2395 | ||
2392 | 2396 | ||
2393 | /** | 2397 | /** |
2394 | * We've been asked to switch addresses, and just now | 2398 | * Check if the given @a address is the same that we are already |
2395 | * got the result from the blacklist check to see if this | 2399 | * using for the respective neighbour. If so, update the bandwidth |
2396 | * is allowed. | 2400 | * assignment and possibly the session and return #GNUNET_OK. |
2401 | * If the new address is different from what the neighbour is | ||
2402 | * using right now, return #GNUNET_NO. | ||
2403 | * | ||
2404 | * @param address address of the other peer, | ||
2405 | * @param session session to use or NULL if transport should initiate a session | ||
2406 | * @param bandwidth_in inbound quota to be used when connection is up, | ||
2407 | * 0 to disconnect from peer | ||
2408 | * @param bandwidth_out outbound quota to be used when connection is up, | ||
2409 | * 0 to disconnect from peer | ||
2410 | * @return #GNUNET_OK if we were able to just update the bandwidth and session, | ||
2411 | * #GNUNET_NO if more extensive changes are required (address changed) | ||
2412 | */ | ||
2413 | static int | ||
2414 | try_run_fast_ats_update (const struct GNUNET_HELLO_Address *address, | ||
2415 | struct Session *session, | ||
2416 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, | ||
2417 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out) | ||
2418 | { | ||
2419 | struct NeighbourMapEntry *n; | ||
2420 | int connected; | ||
2421 | |||
2422 | n = lookup_neighbour (&address->peer); | ||
2423 | if ( (NULL == n) || | ||
2424 | (NULL == n->primary_address.address) || | ||
2425 | (0 != GNUNET_HELLO_address_cmp (address, | ||
2426 | n->primary_address.address)) ) | ||
2427 | return GNUNET_NO; | ||
2428 | /* We are not really switching addresses, but merely adjusting | ||
2429 | session and/or bandwidth, can do fast ATS update! */ | ||
2430 | if (session != n->primary_address.session) | ||
2431 | { | ||
2432 | /* switch to a different session, but keeping same address; could | ||
2433 | happen if there is a 2nd inbound connection */ | ||
2434 | connected = GNUNET_TRANSPORT_is_connected (n->state); | ||
2435 | if (GNUNET_YES == connected) | ||
2436 | GST_ats_set_in_use (n->primary_address.address, | ||
2437 | n->primary_address.session, | ||
2438 | GNUNET_NO); | ||
2439 | n->primary_address.session = session; | ||
2440 | if (GNUNET_YES == connected) | ||
2441 | GST_ats_set_in_use (n->primary_address.address, | ||
2442 | n->primary_address.session, | ||
2443 | GNUNET_YES); | ||
2444 | } | ||
2445 | n->primary_address.bandwidth_in = bandwidth_in; | ||
2446 | n->primary_address.bandwidth_out = bandwidth_out; | ||
2447 | GST_neighbours_set_incoming_quota (&address->peer, | ||
2448 | bandwidth_in); | ||
2449 | send_outbound_quota (&address->peer, | ||
2450 | bandwidth_out); | ||
2451 | return GNUNET_OK; | ||
2452 | } | ||
2453 | |||
2454 | |||
2455 | /** | ||
2456 | * We've been asked to switch addresses, and just now got the result | ||
2457 | * from the blacklist check to see if this is allowed. | ||
2397 | * | 2458 | * |
2398 | * @param cls the `struct BlacklistCheckSwitchContext` with | 2459 | * @param cls the `struct BlacklistCheckSwitchContext` with |
2399 | * the information about the future address | 2460 | * the information about the future address |
@@ -2410,62 +2471,28 @@ switch_address_bl_check_cont (void *cls, | |||
2410 | struct GNUNET_TRANSPORT_PluginFunctions *papi; | 2471 | struct GNUNET_TRANSPORT_PluginFunctions *papi; |
2411 | struct NeighbourMapEntry *n; | 2472 | struct NeighbourMapEntry *n; |
2412 | 2473 | ||
2413 | papi = GST_plugins_find (blc_ctx->address->transport_name); | 2474 | if (result == GNUNET_NO) |
2414 | |||
2415 | if ( (NULL == (n = lookup_neighbour (peer))) || | ||
2416 | (result == GNUNET_NO) || | ||
2417 | (NULL == papi) ) | ||
2418 | { | 2475 | { |
2419 | if (NULL == n) | 2476 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
2420 | { | 2477 | "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n", |
2421 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 2478 | GST_plugins_a2s (blc_ctx->address), |
2422 | "Peer %s is unknown, suggestion ignored\n", | 2479 | blc_ctx->session, |
2423 | GNUNET_i2s (peer)); | 2480 | GNUNET_i2s (&blc_ctx->address->peer)); |
2424 | GNUNET_STATISTICS_update (GST_stats, | 2481 | GNUNET_STATISTICS_update (GST_stats, |
2425 | "# ATS suggestions ignored (neighbour unknown)", | 2482 | "# ATS suggestions ignored (blacklist denied)", |
2426 | 1, | 2483 | 1, |
2427 | GNUNET_NO); | 2484 | GNUNET_NO); |
2428 | } | ||
2429 | if (result == GNUNET_NO) | ||
2430 | { | ||
2431 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2432 | "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n", | ||
2433 | GST_plugins_a2s (blc_ctx->address), | ||
2434 | blc_ctx->session, | ||
2435 | GNUNET_i2s (&blc_ctx->address->peer)); | ||
2436 | GNUNET_STATISTICS_update (GST_stats, | ||
2437 | "# ATS suggestions ignored (blacklist denied)", | ||
2438 | 1, | ||
2439 | GNUNET_NO); | ||
2440 | } | ||
2441 | if (NULL == papi) | ||
2442 | { | ||
2443 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2444 | "Plugin `%s' for suggested address `%s' session %p for peer `%s' is not available\n", | ||
2445 | blc_ctx->address->transport_name, | ||
2446 | GST_plugins_a2s (blc_ctx->address), | ||
2447 | blc_ctx->session, | ||
2448 | GNUNET_i2s (&blc_ctx->address->peer)); | ||
2449 | GNUNET_STATISTICS_update (GST_stats, | ||
2450 | "# ATS suggestions ignored (plugin unknown)", | ||
2451 | 1, | ||
2452 | GNUNET_NO); | ||
2453 | } | ||
2454 | |||
2455 | /* This address is blacklisted, delete session */ | ||
2456 | /* FIXME: tell plugin to force killing session here and now! */ | 2485 | /* FIXME: tell plugin to force killing session here and now! */ |
2457 | 2486 | /* FIXME: Let ATS know that the suggested address did not work! */ | |
2458 | /* Remove blacklist check and clean up */ | 2487 | goto cleanup; |
2459 | GNUNET_CONTAINER_DLL_remove (pending_bc_head, | ||
2460 | pending_bc_tail, | ||
2461 | blc_ctx); | ||
2462 | GNUNET_HELLO_address_free (blc_ctx->address); | ||
2463 | GNUNET_free (blc_ctx); | ||
2464 | return; | ||
2465 | } | 2488 | } |
2466 | 2489 | ||
2490 | papi = GST_plugins_find (blc_ctx->address->transport_name); | ||
2491 | GNUNET_assert (NULL != papi); | ||
2492 | |||
2467 | if (NULL == blc_ctx->session) | 2493 | if (NULL == blc_ctx->session) |
2468 | { | 2494 | { |
2495 | /* need to create a session, ATS only gave us an address */ | ||
2469 | blc_ctx->session = papi->get_session (papi->cls, | 2496 | blc_ctx->session = papi->get_session (papi->cls, |
2470 | blc_ctx->address); | 2497 | blc_ctx->address); |
2471 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 2498 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
@@ -2476,10 +2503,10 @@ switch_address_bl_check_cont (void *cls, | |||
2476 | if (NULL != blc_ctx->session) | 2503 | if (NULL != blc_ctx->session) |
2477 | GST_ats_new_session (blc_ctx->address, | 2504 | GST_ats_new_session (blc_ctx->address, |
2478 | blc_ctx->session); | 2505 | blc_ctx->session); |
2479 | |||
2480 | } | 2506 | } |
2481 | if (NULL == blc_ctx->session) | 2507 | if (NULL == blc_ctx->session) |
2482 | { | 2508 | { |
2509 | /* session creation failed, bad!, fail! */ | ||
2483 | GNUNET_STATISTICS_update (GST_stats, | 2510 | GNUNET_STATISTICS_update (GST_stats, |
2484 | "# ATS suggestions ignored (failed to create session)", | 2511 | "# ATS suggestions ignored (failed to create session)", |
2485 | 1, | 2512 | 1, |
@@ -2489,47 +2516,25 @@ switch_address_bl_check_cont (void *cls, | |||
2489 | "Failed to obtain new session for peer `%s' and address '%s'\n", | 2516 | "Failed to obtain new session for peer `%s' and address '%s'\n", |
2490 | GNUNET_i2s (&blc_ctx->address->peer), | 2517 | GNUNET_i2s (&blc_ctx->address->peer), |
2491 | GST_plugins_a2s (blc_ctx->address)); | 2518 | GST_plugins_a2s (blc_ctx->address)); |
2492 | /* FIXME: Delete address in ATS!? */ | 2519 | /* FIXME: Let ATS know that the suggested address did not work! */ |
2493 | GNUNET_CONTAINER_DLL_remove (pending_bc_head, | 2520 | goto cleanup; |
2494 | pending_bc_tail, | ||
2495 | blc_ctx); | ||
2496 | GNUNET_HELLO_address_free (blc_ctx->address); | ||
2497 | GNUNET_free (blc_ctx); | ||
2498 | return; | ||
2499 | } | 2521 | } |
2500 | 2522 | ||
2501 | if ( (NULL != n->primary_address.address) && | 2523 | /* We did this check already before going into blacklist, but |
2502 | (0 == GNUNET_HELLO_address_cmp (blc_ctx->address, | 2524 | it is theoretically possible that the situation changed in |
2503 | n->primary_address.address)) ) | 2525 | the meantime, hence we check again here */ |
2504 | { | 2526 | if (GNUNET_OK == |
2505 | if (blc_ctx->session == n->primary_address.session) | 2527 | try_run_fast_ats_update (blc_ctx->address, |
2506 | { | 2528 | blc_ctx->session, |
2507 | // FIXME: handle this before blacklist check! | 2529 | blc_ctx->bandwidth_in, |
2508 | /* This address is already primary, update only quotas */ | 2530 | blc_ctx->bandwidth_out)) |
2509 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 2531 | goto cleanup; /* was just a minor update, we're done */ |
2510 | "Updating quota for peer `%s' address `%s' session %p\n", | ||
2511 | GNUNET_i2s (&blc_ctx->address->peer), | ||
2512 | GST_plugins_a2s (blc_ctx->address), | ||
2513 | blc_ctx->session); | ||
2514 | set_primary_address (n, | ||
2515 | blc_ctx->address, | ||
2516 | blc_ctx->session, | ||
2517 | blc_ctx->bandwidth_in, | ||
2518 | blc_ctx->bandwidth_out, | ||
2519 | GNUNET_NO); | ||
2520 | 2532 | ||
2521 | GNUNET_CONTAINER_DLL_remove (pending_bc_head, | 2533 | /* check if we also need to setup the neighbour entry */ |
2522 | pending_bc_tail, | 2534 | if (NULL == (n = lookup_neighbour (peer))) |
2523 | blc_ctx); | 2535 | { |
2524 | GNUNET_HELLO_address_free (blc_ctx->address); | 2536 | n = setup_neighbour (peer); |
2525 | GNUNET_free (blc_ctx); | 2537 | n->state = GNUNET_TRANSPORT_PS_INIT_ATS; |
2526 | return; | ||
2527 | } | ||
2528 | // FIXME: is this really OK? | ||
2529 | GNUNET_STATISTICS_update (GST_stats, | ||
2530 | "# ATS suggestion oddity (address match, session missmatch)", | ||
2531 | 1, | ||
2532 | GNUNET_NO); | ||
2533 | } | 2538 | } |
2534 | 2539 | ||
2535 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 2540 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
@@ -2553,7 +2558,7 @@ switch_address_bl_check_cont (void *cls, | |||
2553 | blc_ctx->bandwidth_in, | 2558 | blc_ctx->bandwidth_in, |
2554 | blc_ctx->bandwidth_out, | 2559 | blc_ctx->bandwidth_out, |
2555 | GNUNET_NO); | 2560 | GNUNET_NO); |
2556 | if ( (ACK_SEND_SYN_ACK == n->ack_state) ) | 2561 | if (ACK_SEND_SYN_ACK == n->ack_state) |
2557 | { | 2562 | { |
2558 | /* Send pending SYN_ACK message */ | 2563 | /* Send pending SYN_ACK message */ |
2559 | n->ack_state = ACK_SEND_ACK; | 2564 | n->ack_state = ACK_SEND_ACK; |
@@ -2731,7 +2736,7 @@ switch_address_bl_check_cont (void *cls, | |||
2731 | GNUNET_break (0); | 2736 | GNUNET_break (0); |
2732 | break; | 2737 | break; |
2733 | } | 2738 | } |
2734 | 2739 | cleanup: | |
2735 | GNUNET_CONTAINER_DLL_remove (pending_bc_head, | 2740 | GNUNET_CONTAINER_DLL_remove (pending_bc_head, |
2736 | pending_bc_tail, | 2741 | pending_bc_tail, |
2737 | blc_ctx); | 2742 | blc_ctx); |
@@ -2759,7 +2764,6 @@ GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address, | |||
2759 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, | 2764 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, |
2760 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out) | 2765 | struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out) |
2761 | { | 2766 | { |
2762 | struct NeighbourMapEntry *n; | ||
2763 | struct GST_BlacklistCheck *blc; | 2767 | struct GST_BlacklistCheck *blc; |
2764 | struct BlacklistCheckSwitchContext *blc_ctx; | 2768 | struct BlacklistCheckSwitchContext *blc_ctx; |
2765 | 2769 | ||
@@ -2767,17 +2771,12 @@ GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address, | |||
2767 | "ATS has decided on an address for peer %s\n", | 2771 | "ATS has decided on an address for peer %s\n", |
2768 | GNUNET_i2s (&address->peer)); | 2772 | GNUNET_i2s (&address->peer)); |
2769 | GNUNET_assert (NULL != address->transport_name); | 2773 | GNUNET_assert (NULL != address->transport_name); |
2770 | if (NULL == (n = lookup_neighbour (&address->peer))) | 2774 | if (GNUNET_OK == |
2771 | { | 2775 | try_run_fast_ats_update (address, |
2772 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 2776 | session, |
2773 | "Peer %s is unknown, suggestion ignored\n", | 2777 | bandwidth_in, |
2774 | GNUNET_i2s (&address->peer)); | 2778 | bandwidth_out)) |
2775 | GNUNET_STATISTICS_update (GST_stats, | ||
2776 | "# ATS suggestions ignored (neighbour unknown)", | ||
2777 | 1, | ||
2778 | GNUNET_NO); | ||
2779 | return; | 2779 | return; |
2780 | } | ||
2781 | 2780 | ||
2782 | /* Check if plugin is available */ | 2781 | /* Check if plugin is available */ |
2783 | if (NULL == (GST_plugins_find (address->transport_name))) | 2782 | if (NULL == (GST_plugins_find (address->transport_name))) |
@@ -2796,18 +2795,12 @@ GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address, | |||
2796 | } | 2795 | } |
2797 | 2796 | ||
2798 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 2797 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
2799 | "ATS suggests %s address '%s' session %p for " | 2798 | "ATS suggests %s address '%s' for peer `%s'\n", |
2800 | "peer `%s' in state %s/%s \n", | ||
2801 | GNUNET_HELLO_address_check_option (address, | 2799 | GNUNET_HELLO_address_check_option (address, |
2802 | GNUNET_HELLO_ADDRESS_INFO_INBOUND) ? "inbound" : "outbound", | 2800 | GNUNET_HELLO_ADDRESS_INFO_INBOUND) |
2801 | ? "inbound" : "outbound", | ||
2803 | GST_plugins_a2s (address), | 2802 | GST_plugins_a2s (address), |
2804 | session, | 2803 | GNUNET_i2s (&address->peer)); |
2805 | GNUNET_i2s (&address->peer), | ||
2806 | GNUNET_TRANSPORT_ps2s (n->state), | ||
2807 | print_ack_state (n->ack_state)); | ||
2808 | |||
2809 | // FIXME: definitively do NOT do this if the | ||
2810 | // suggested address did not change!!! | ||
2811 | 2804 | ||
2812 | /* Perform blacklist check */ | 2805 | /* Perform blacklist check */ |
2813 | blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext); | 2806 | blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext); |
@@ -2897,7 +2890,7 @@ send_utilization_data (void *cls, | |||
2897 | n->util_payload_bytes_sent = 0; | 2890 | n->util_payload_bytes_sent = 0; |
2898 | n->util_total_bytes_recv = 0; | 2891 | n->util_total_bytes_recv = 0; |
2899 | n->util_total_bytes_sent = 0; | 2892 | n->util_total_bytes_sent = 0; |
2900 | n->last_util_transmission = GNUNET_TIME_absolute_get(); | 2893 | n->last_util_transmission = GNUNET_TIME_absolute_get (); |
2901 | return GNUNET_OK; | 2894 | return GNUNET_OK; |
2902 | } | 2895 | } |
2903 | 2896 | ||
@@ -2933,7 +2926,7 @@ GST_neighbours_notify_data_recv (const struct GNUNET_HELLO_Address *address, | |||
2933 | n = lookup_neighbour (&address->peer); | 2926 | n = lookup_neighbour (&address->peer); |
2934 | if (NULL == n) | 2927 | if (NULL == n) |
2935 | return; | 2928 | return; |
2936 | n->util_total_bytes_recv += ntohs(message->size); | 2929 | n->util_total_bytes_recv += ntohs (message->size); |
2937 | } | 2930 | } |
2938 | 2931 | ||
2939 | 2932 | ||
@@ -2947,7 +2940,7 @@ GST_neighbours_notify_payload_recv (const struct GNUNET_HELLO_Address *address, | |||
2947 | n = lookup_neighbour (&address->peer); | 2940 | n = lookup_neighbour (&address->peer); |
2948 | if (NULL == n) | 2941 | if (NULL == n) |
2949 | return; | 2942 | return; |
2950 | n->util_payload_bytes_recv += ntohs(message->size); | 2943 | n->util_payload_bytes_recv += ntohs (message->size); |
2951 | } | 2944 | } |
2952 | 2945 | ||
2953 | 2946 | ||
@@ -2972,6 +2965,7 @@ GST_neighbours_notify_payload_sent (const struct GNUNET_PeerIdentity *peer, | |||
2972 | size_t size) | 2965 | size_t size) |
2973 | { | 2966 | { |
2974 | struct NeighbourMapEntry *n; | 2967 | struct NeighbourMapEntry *n; |
2968 | |||
2975 | n = lookup_neighbour (peer); | 2969 | n = lookup_neighbour (peer); |
2976 | if (NULL == n) | 2970 | if (NULL == n) |
2977 | return; | 2971 | return; |
@@ -3850,7 +3844,8 @@ GST_neighbours_start (unsigned int max_fds) | |||
3850 | { | 3844 | { |
3851 | neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO); | 3845 | neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO); |
3852 | util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL, | 3846 | util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL, |
3853 | utilization_transmission, NULL); | 3847 | &utilization_transmission, |
3848 | NULL); | ||
3854 | } | 3849 | } |
3855 | 3850 | ||
3856 | 3851 | ||