LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { int64_t paint_time = get_nanoseconds(); switch(msg) { case WM_DESTROY: PostQuitMessage(0); break; case WM_MOUSEWHEEL: scrolls++; InvalidateRect(hwnd, NULL, false); break; case WM_KEYDOWN: if (wParam == VK_ESCAPE) { esc_presses++; } key_downs++; InvalidateRect(hwnd, NULL, false); break; case WM_PAINT: PAINTSTRUCT ps; BeginPaint(hwnd, &ps); wglMakeCurrent(ps.hdc, context); draw_pattern_with_opengl(pattern, scrolls, key_downs, esc_presses); SwapBuffers(ps.hdc); EndPaint(hwnd, &ps); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; }
/** * repad_net_analyze_packet_before_put_buffer - Get some information from * received packet before put it in the buffer * * @param skb The received packet */ static void repad_net_analyze_packet_before_put_in_buffer(struct sk_buff *skb) { /* Monitors viewed prefixes */ if (!SKB_REPA_HEADER(skb)->flag_hide) { repad_add_new_node(ntohl(SKB_REPA_HEADER_V2(skb)->prefix_src), get_nanoseconds()); // Keep prefix information about nodes on network } }
static screenshot *take_screenshot_with_gdi(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { HRESULT hr; int ir; BOOL r; // DWM causes screenshotting with GDI to be very slow. The only fix is to // disable DWM during the test. DWM will automatically be re-enabled when the // process exits. This API stops working on Windows 8 (though it returns // success regardless). if (!composition_disabled) { // HACK: DwmEnableComposition fails unless GetDC(NULL) has been called // first. Who knows why. HDC workaround = GetDC(NULL); int ir = ReleaseDC(NULL, workaround); assert(ir); hr = DwmEnableComposition(DWM_EC_DISABLECOMPOSITION); assert(hr == S_OK); // DWM takes a little while to repain the screen after being disabled. Sleep(1000); composition_disabled = true; } int virtual_x = ((int)x) - GetSystemMetrics(SM_XVIRTUALSCREEN); int virtual_y = ((int)y) - GetSystemMetrics(SM_YVIRTUALSCREEN); width = min(width, (uint32_t)GetSystemMetrics(SM_CXVIRTUALSCREEN)); height = min(height, (uint32_t)GetSystemMetrics(SM_CYVIRTUALSCREEN)); HDC screen_dc = GetDC(NULL); assert(screen_dc); HDC memory_dc = CreateCompatibleDC(screen_dc); assert(memory_dc); BITMAPINFO bitmap_info; memset(&bitmap_info, 0, sizeof(BITMAPINFO)); bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmap_info.bmiHeader.biWidth = width; bitmap_info.bmiHeader.biHeight = -(int)height; bitmap_info.bmiHeader.biPlanes = 1; bitmap_info.bmiHeader.biBitCount = 32; bitmap_info.bmiHeader.biCompression = BI_RGB; uint8_t *pixels = NULL; HBITMAP hbitmap = CreateDIBSection(screen_dc, &bitmap_info, DIB_RGB_COLORS, (void **)&pixels, NULL, 0); assert(hbitmap); SelectObject(memory_dc, hbitmap); r = BitBlt(memory_dc, 0, 0, width, height, screen_dc, virtual_x, virtual_y, SRCCOPY); assert(r); ir = ReleaseDC(NULL, screen_dc); assert(ir); r = DeleteObject(memory_dc); assert(r); screenshot *shot = (screenshot *)malloc(sizeof(screenshot)); shot->pixels = pixels; shot->width = width; shot->height = height; shot->stride = width * 4; shot->platform_specific_data = hbitmap; shot->time_nanoseconds = get_nanoseconds(); return shot; }
void calc_send(repa_sock_t *sock, const char* interest, size_t packet_size, const int wait_time, const int64_t total_bytes) { long long int start_time, end_time, diff_time; int64_t total_bytes_sent = 0; { size_t max_packet_size = LIBREPA_MAX_DATA_LEN(strlen(interest)); packet_size = (packet_size > max_packet_size ? max_packet_size : packet_size); } char data[packet_size]; /* initialize random seed: */ srand (time(NULL)); printf("Starting sending %"PRId64" [%zd]...\n", total_bytes, sizeof(int64_t)); repa_send(sock, interest, &total_bytes, sizeof(int64_t), 0); start_time = get_nanoseconds(); while (true) { if (repa_send(sock, interest, data, packet_size, 0) > 0) { total_bytes_sent += packet_size; if (total_bytes_sent >= total_bytes) { // Finalizando o envio break; } } else { // Erro ao tentar enviar pacote break; } if (wait_time == 1) usleep((rand()%8000)+2500); //printf("\rSent %jd bytes...[%jd%%]", total_bytes_sent, /// (total_bytes_sent*100)/total_bytes); // fflush(stdout); } end_time = get_nanoseconds(); diff_time = end_time - start_time; double kbps = (((double)total_bytes_sent)/((double)diff_time))*((double)NSEC_PER_SEC/(double)1024); printf("\rSent %ju*%zd=%jd bytes in %llds %lldns -> %.2fKB/s\n", total_bytes_sent/packet_size, packet_size, total_bytes_sent, (diff_time/NSEC_PER_SEC), (diff_time%NSEC_PER_SEC), kbps); }
void calc_recv(repa_sock_t *sock) { prefix_addr_t src_address = 0; int64_t num_packets = 0, bytes_recv = 0, total_bytes_recv = 0, total_bytes = INT64_MAX; long long int start_time, end_time, diff_time; char interest[LIBREPA_MAX_INTEREST_LEN], data[LIBREPA_MAX_DATA_LEN(0)]; printf("Waiting to start...\n"); bytes_recv = (int64_t)repa_recv(sock, interest, data, LIBREPA_MAX_DATA_LEN(0), &src_address, NULL); if (bytes_recv == sizeof(int64_t)) { memcpy(&total_bytes, data, sizeof(int64_t)); } printf("Receiving...\n"); start_time = get_nanoseconds(); while (true) { if ((bytes_recv = repa_timed_recv(sock, interest, data, LIBREPA_MAX_DATA_LEN(0), &src_address, NULL, 1*USEC_PER_SEC)) > 0) { num_packets++; total_bytes_recv += bytes_recv; } else { // Nenhum byte recebido no intervalo de tempo de 1seg // indica o fim do recebimento break; } //printf("\rReceive %jd bytes...[%jd%%]", total_bytes_recv, // (total_bytes_recv*100)/total_bytes); // fflush(stdout); } end_time = get_nanoseconds(); diff_time = end_time - start_time; // Retira 1 segundo de espera caso não tenha recebido // o byte de finalização do fluxo diff_time -= 1*NSEC_PER_SEC; double kbps = (((double)total_bytes_recv)/((double)diff_time))*((double)NSEC_PER_SEC/(double)1024); num_packets = (num_packets == 0 ? INT64_MAX : num_packets); total_bytes = (total_bytes == 0 ? INT64_MAX : total_bytes); printf("\rReceived %jd*%jd=%jd/%jd bytes in %llds %lldns -> %.2fKB/s - %jd%% loss\n", num_packets, total_bytes_recv/num_packets, total_bytes_recv, total_bytes, (diff_time/NSEC_PER_SEC), (diff_time%NSEC_PER_SEC), kbps, 100-((total_bytes_recv*100)/total_bytes)); }
// Updates the given pattern with the given event data, then draws the pattern // to the current OpenGL context. void draw_pattern_with_opengl(uint8_t pattern[], int scroll_events, int keydown_events, int esc_presses) { int64_t time = get_nanoseconds(); if (last_draw_time > 0) { if (time - last_draw_time > biggest_draw_time_gap) { biggest_draw_time_gap = time - last_draw_time; debug_log("New biggest draw time gap: %f ms.", biggest_draw_time_gap / (double)nanoseconds_per_millisecond); } } last_draw_time = time; if (esc_presses == 0) { pattern[4 * 4 + 2] = TEST_MODE_JAVASCRIPT_LATENCY; } else { pattern[4 * 4 + 2] = TEST_MODE_ABORT; } // Update the pattern with the number of scroll events mod 255. pattern[4 * 5] = pattern[4 * 5 + 1] = pattern[4 * 5 + 2] = scroll_events; // Update the pattern with the number of keydown events mod 255. pattern[4 * 4 + 1] = keydown_events; // Increment the "JavaScript frames" counter. pattern[4 * 4 + 0]++; // Increment the "CSS animation frames" counter. pattern[4 * 6 + 0]++; pattern[4 * 6 + 1]++; pattern[4 * 6 + 2]++; GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); GLint height = viewport[3]; glDisable(GL_SCISSOR_TEST); // Alternate background each frame to make tearing easy to spot. float background = 1; if (pattern[4 * 4 + 0] % 2 == 1) background = 0.8; glClearColor(background, background, background, 1); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); for (int i = 0; i < pattern_bytes; i += 4) { glClearColor(pattern[i + 2] / 255.0, pattern[i + 1] / 255.0, pattern[i] / 255.0, 1); glScissor(i / 4, height - 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); } }
// Main test function. Locates the given magic pixel pattern on the screen, then // runs one full latency test, sending input events and recording responses. On // success, the results of the test are reported in the output parameters, and // true is returned. If the test fails, the error parameter is filled in with // an error message and false is returned. bool measure_latency( const uint8_t magic_pattern[], double *out_key_down_latency_ms, double *out_scroll_latency_ms, double *out_max_js_pause_time_ms, double *out_max_css_pause_time_ms, double *out_max_scroll_pause_time_ms, char **error) { screenshot *screenshot = take_screenshot(0, 0, UINT32_MAX, UINT32_MAX); if (!screenshot) { *error = "Failed to take screenshot."; return false; } assert(screenshot->width > 0 && screenshot->height > 0); size_t x, y; bool found_pattern = find_pattern(magic_pattern, screenshot, &x, &y); free_screenshot(screenshot); if (!found_pattern) { *error = "Failed to find test pattern on screen."; return false; } uint8_t full_pattern[pattern_bytes]; for (int i = 0; i < pattern_magic_bytes; i++) { full_pattern[i] = magic_pattern[i]; } measurement_t measurement; measurement_t previous_measurement; memset(&measurement, 0, sizeof(measurement_t)); memset(&previous_measurement, 0, sizeof(measurement_t)); int screenshots = 0; bool first_screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!first_screenshot_successful) { *error = "Failed to read data from test pattern."; return false; } if (measurement.test_mode == TEST_MODE_NATIVE_REFERENCE) { uint8_t *test_pattern = (uint8_t *)malloc(pattern_bytes); memset(test_pattern, 0, pattern_bytes); for (int i = 0; i < pattern_magic_bytes; i++) { test_pattern[i] = rand(); } if (!open_native_reference_window(test_pattern)) { *error = "Failed to open native reference window."; return false; } bool return_value = measure_latency(test_pattern, out_key_down_latency_ms, out_scroll_latency_ms, out_max_js_pause_time_ms, out_max_css_pause_time_ms, out_max_scroll_pause_time_ms, error); if (!close_native_reference_window()) { debug_log("Failed to close native reference window."); }; return return_value; } int64_t start_time = measurement.screenshot_time; previous_measurement = measurement; statistic javascript_frames; statistic css_frames; statistic key_down_events; statistic scroll_stats; init_statistic("javascript_frames", &javascript_frames, measurement.javascript_frames, start_time); init_statistic("key_down_events", &key_down_events, measurement.key_down_events, start_time); init_statistic("css_frames", &css_frames, measurement.css_frames, start_time); init_statistic("scroll", &scroll_stats, measurement.scroll_position, start_time); int sent_events = 0; int scroll_x = x + 40; int scroll_y = y + 40; int64_t last_scroll_sent = start_time; if (measurement.test_mode == TEST_MODE_SCROLL_LATENCY) { send_scroll_down(scroll_x, scroll_y); scroll_stats.previous_change_time = get_nanoseconds(); } while(true) { bool screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!screenshot_successful) { *error = "Test window moved during test. The test window must remain " "stationary and focused during the entire test."; return false; } if (measurement.test_mode == TEST_MODE_ABORT) { *error = "Test aborted."; return false; } screenshots++; int64_t screenshot_time = measurement.screenshot_time; int64_t previous_screenshot_time = previous_measurement.screenshot_time; debug_log("screenshot time %f", (screenshot_time - previous_screenshot_time) / (double)nanoseconds_per_millisecond); update_statistic(&javascript_frames, measurement.javascript_frames, screenshot_time, previous_screenshot_time); update_statistic(&key_down_events, measurement.key_down_events, screenshot_time, previous_screenshot_time); update_statistic(&css_frames, measurement.css_frames, screenshot_time, previous_screenshot_time); bool scroll_updated = update_statistic(&scroll_stats, measurement.scroll_position, screenshot_time, previous_screenshot_time); if (measurement.test_mode == TEST_MODE_JAVASCRIPT_LATENCY) { if (key_down_events.measurements >= latency_measurements_to_take) { break; } if (key_down_events.value_delta > sent_events) { *error = "More events received than sent! This is probably a bug in " "the test."; return false; } if (screenshot_time - key_down_events.previous_change_time > event_response_timeout_ms * nanoseconds_per_millisecond) { *error = "Browser did not respond to keyboard input. Make sure the " "test page remains focused for the entire test."; return false; } if (key_down_events.value_delta == sent_events) { // We want to avoid sending input events at a predictable time relative // to frames, so introduce a random delay of up to 1 frame (16.67 ms) // before sending the next event. usleep((rand() % 17) * 1000); if (!send_keystroke_z()) { *error = "Failed to send keystroke for \"Z\" key to test window."; return false; } key_down_events.previous_change_time = get_nanoseconds(); sent_events++; } } else if (measurement.test_mode == TEST_MODE_SCROLL_LATENCY) { if (scroll_stats.measurements >= latency_measurements_to_take) { break; } if (screenshot_time - scroll_stats.previous_change_time > event_response_timeout_ms * nanoseconds_per_millisecond) { *error = "Browser did not respond to scroll events. Make sure the " "test page remains focused for the entire test."; return false; } if (scroll_updated) { // We saw the start of a scroll. Wait for the scroll animation to // finish before continuing. We assume the animation is finished if // it's been 100 milliseconds since we last saw the scroll position // change. int64_t scroll_update_time = screenshot_time; int64_t scroll_wait_start_time = screenshot_time; while (screenshot_time - scroll_update_time < 100 * nanoseconds_per_millisecond) { screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!screenshot_successful) { *error = "Test window moved during test. The test window must " "remain stationary and focused during the entire test."; return false; } screenshot_time = measurement.screenshot_time; if (screenshot_time - scroll_wait_start_time > nanoseconds_per_second) { *error = "Browser kept scrolling for more than 1 second after a " "single scrollwheel event."; return false; } if (measurement.scroll_position != scroll_stats.value) { scroll_stats.value = measurement.scroll_position; scroll_update_time = screenshot_time; } } // We want to avoid sending input events at a predictable time // relative to frames, so introduce a random delay of up to 1 frame // (16.67 ms) before sending the next event. usleep((rand() % 17) * 1000); send_scroll_down(scroll_x, scroll_y); scroll_stats.previous_change_time = get_nanoseconds(); } } else if (measurement.test_mode == TEST_MODE_PAUSE_TIME) { // For the pause time test we want the browser to scroll continuously. // Send a scroll event every frame. if (screenshot_time - last_scroll_sent > 17 * nanoseconds_per_millisecond) { send_scroll_down(scroll_x, scroll_y); last_scroll_sent = get_nanoseconds(); } } else if (measurement.test_mode == TEST_MODE_PAUSE_TIME_TEST_FINISHED) { break; } else { *error = "Invalid test type. This is a bug in the test."; return false; } if (screenshot_time - start_time > test_timeout_ms * nanoseconds_per_millisecond) { *error = "Timeout."; return false; } previous_measurement = measurement; usleep(0); } // The latency we report is the midpoint of the interval given by the average // upper and lower bounds we've computed. *out_key_down_latency_ms = (upper_bound_ms(&key_down_events) + lower_bound_ms(&key_down_events)) / 2; *out_scroll_latency_ms = (upper_bound_ms(&scroll_stats) + lower_bound_ms(&scroll_stats) / 2); *out_max_js_pause_time_ms = javascript_frames.max_lower_bound / (double) nanoseconds_per_millisecond; *out_max_css_pause_time_ms = css_frames.max_lower_bound / (double) nanoseconds_per_millisecond; *out_max_scroll_pause_time_ms = scroll_stats.max_lower_bound / (double) nanoseconds_per_millisecond; debug_log("out_key_down_latency_ms: %f out_scroll_latency_ms: %f " "out_max_js_pause_time_ms: %f out_max_css_pause_time: %f\n " "out_max_scroll_pause_time_ms: %f", *out_key_down_latency_ms, *out_scroll_latency_ms, *out_max_js_pause_time_ms, *out_max_css_pause_time_ms, *out_max_scroll_pause_time_ms); return true; }
/** * repad_net_handle_msg - Handle a new packet received with repa version 2 * * @param skb The new packet received to be handle */ void repad_net_handle_msg_v2(struct sk_buff *skb) { int64_t last_timestamp = 0, now = get_nanoseconds(); prefix_addr_t prefix_src = ntohl(SKB_REPA_HEADER_V2(skb)->prefix_src); prefix_addr_t prefix_dst = ntohl(SKB_REPA_HEADER_V2(skb)->prefix_dst); { char prefix[64]; repad_print_prefix(prefix_src, prefix); repad_printf(LOG_INFO, "HANDLE_V2: Just arrive a packet from %s!\n", prefix); } /* Just for statistics, increase total messages received */ repad_attr.statistics.qtyMessageTotalReceived++; /* Verify the destination of packet */ repad_printf(LOG_INFO, "HANDLE_V2: Verify destination prefix of packet!\n"); if (!((prefix_dst == 0) /* Group communication */ || (prefix_dst == repad_attr.pref_node_address))) { /* Addressed to me */ repad_printf(LOG_INFO, "HANDLE_V2: Packet dropped because it is not for me!\n"); goto exit; } /* Verify memory */ repad_printf(LOG_INFO, "HANDLE_V2: Search packet in the node memory!\n"); if ((last_timestamp = repad_verify_memory(skb)) > 0L) { /* If received packet will be discarded, calculate RTT */ if ((0 < last_timestamp) && (last_timestamp < INT64_MAX)) { int64_t diff = now - last_timestamp; if (diff > 0) { // Valid diff (now > last_timestamp) rtt_mean_estimator_measurement(repad_attr.rtt_estimator, diff); } } /* Statistics and drop packet by memory */ repad_attr.statistics.qtyMessageDroppedMemory++; // Statistics repad_printf(LOG_INFO, "HANDLE_V2: Packet dropped by memory!\n"); goto exit; // End. } // Verify extension headers repad_printf(LOG_INFO, "HANDLE_V2: Evaluate extension headers!\n"); intptr_t header_position = sizeof(struct repahdr_v2); uint8_t next_header = SKB_REPA_HEADER_V2(skb)->next_header; while(next_header != REPAHDR_EH_NO_NEXT) { switch (next_header) { case REPAHDR_EH_INTEREST: // A Interest Packet /* Evaluate Prefixes for matching and HTL */ repad_net_evaluate_prefix_htl(skb, prefix_src); // Handle interest packet and return the next header next_header = repad_net_handle_eh_interest(skb, &header_position); break; case REPAHDR_CONTROL: // A Control Packet next_header = repad_net_handle_control_messages(skb, &header_position); break; case REPAHDR_EH_HASH: // Not defined yet case REPAHDR_EH_SECURITY: // Not defined yet default: // TODO: create another header extension next_header = REPAHDR_EH_NO_NEXT; // No next extension header break; } } exit: /* Free the sk_buffer */ skb_release(skb); }
/** * repad_net_handle_eh_interest - Handle the interest extension header * * @param skb The sk_buff with packet received * @param header_position The interest header position after others extension headers * * @return The code of next extension header */ static uint8_t repad_net_handle_eh_interest(struct sk_buff *skb, intptr_t *header_position) { /* Get the Repa header */ struct repahdr_v2 *hdr = SKB_REPA_HEADER_V2(skb); /* Get the Next Header and handle like Interest extension header */ struct repahdr_v2_eh_interest *hdr_eh_interest = (struct repahdr_v2_eh_interest*) ((intptr_t)hdr + (*header_position)); // Update the header position (*header_position) += offsetof(struct repahdr_v2_eh_interest,interest) + hdr_eh_interest->interest_len; /* Create a temporary interest */ char *interest_temp = repad_strclone(hdr_eh_interest->interest, (size_t)hdr_eh_interest->interest_len); uint64_t pkt_timestamp = ntohll(hdr->timestamp); lib_repa_proto_interest_t meta_interest = { .flags = hdr_eh_interest->flags, .interest_len = hdr_eh_interest->interest_len, .split_interest_char = hdr_eh_interest->split_interest_char }; repad_unix_delivery(NULL, skb, &meta_interest, interest_temp, pkt_timestamp); /* Free temporary interest. No one MUST get this reference */ free(interest_temp); return hdr_eh_interest->next_header; } /** * repad_net_evaluate_prefix_htl - function used to evaluate the prefix and htl * for received packets. The algorithm avoid isolation was implemented here too. * * @param skb The new packet received to be handle * @param prefix_src The source prefix of received packet */ static void repad_net_evaluate_prefix_htl(struct sk_buff *skb, prefix_addr_t prefix_src) { repad_printf(LOG_INFO, "HANDLE_V2: Evaluate prefix match and HTL!\n"); if (!repad_net_match_prefix_at_least(prefix_src)) { /* Evaluate the Avoid Isolation algorithm */ if (repad_attr.bl_avoid_isolation_enable) { if (SKB_REPA_HEADER_V2(skb)->htl > 0) { SKB_REPA_HEADER_V2(skb)->htl--; // Decrease HTL repad_printf(LOG_INFO, "AVOID_ISOLATION: Keep packet for a while!\n"); /* Verify if this packet is already scheduled to send later */ pwm_packet_without_match_t *pwm = pwm_create(skb); if (pwm != NULL) { int64_t time_to_sent = get_nanoseconds() + /************************************************ * TODO: Change time to wait before sent a packet ************************************************/ repad_attr.dbl_ai_rtt_factor*rtt_mean_estimator_get_curr_estimate(repad_attr.rtt_estimator) + rand()%500000; // Get a rand time between [0,500]us /* If not found, schedule to send later */ if (tt_timer_schedule(repad_attr.tt_timer_task, pwm->__scheduled_task, time_to_sent, 0) == 0) { if (dll_append(repad_attr.list_pkt_without_match, pwm) == NULL) { /* If was not possible insert in list, release an instance */ // pwm_release(pwm); repad_printf(LOG_ERR, "AVOID_ISOLATION: ERROR: Cannot add new pwm in list_pkt_without_match!\n"); } } else { /* If timer does not schedule the task, destroy pwm */ pwm_destroy(pwm); } } } } else { /* Drop packet right now */ repad_printf(LOG_INFO, "HANDLE_V2: Packet dropped by Prefix!\n"); repad_attr.statistics.qtyMessageDroppedPrefixP++; // Statistics: dropped by prefix } } else if (SKB_REPA_HEADER_V2(skb)->flag_foward) { // If has match and is to forward, forward if (SKB_REPA_HEADER_V2(skb)->htl > 0) { SKB_REPA_HEADER_V2(skb)->htl--; // Decrease HTL /* Evaluate the Reduce Message algorithm */ if (repad_attr.bl_reduce_messages_enable) { repad_printf(LOG_INFO, "REDUCE_MESSAGE: Probability for " "forwarding is %0.2f!\n", mc_get_mean(repad_attr.sent_probability)); double sent_prob = (rand()/(float)RAND_MAX); if (sent_prob <= mc_get_mean(repad_attr.sent_probability)) { // Statistics: packet forwarded repad_attr.statistics.qtyMessageSent++; /* Forward packet right now */ repad_printf(LOG_INFO, "REDUCE_MESSAGE: Packet will be forward!\n"); repad_net_forward_packet(skb); } } else { // Statistics: packet forwarded repad_attr.statistics.qtyMessageSent++; /* Forward packet right now */ repad_printf(LOG_INFO, "HANDLE_V2: Packet will be forward!\n"); repad_net_forward_packet(skb); } } else { repad_printf(LOG_INFO, "HANDLE_V2: Packet dropped by HTL!\n"); repad_attr.statistics.qtyMessageDroppedMaxHTL++; // Statistics: dropped by HTL limit } } // Statistics: Unique packet repad_attr.statistics.qtyMessageUniqueReceive++; }