/** * @brief Test the ROHC library with a flow of ROHC packets * * @param filename The name of the PCAP file that contains the ROHC packets * @param failure_start The first packet that shall fail to be decompressed * @return 0 in case of success, * 1 in case of failure */ static int test_decomp(const char *const filename, const size_t failure_start) { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *handle; int link_layer_type; int link_len; struct pcap_pkthdr header; unsigned char *packet; struct rohc_decomp *decomp; unsigned int counter; int is_failure = 1; /* open the source dump file */ handle = pcap_open_offline(filename, errbuf); if(handle == NULL) { fprintf(stderr, "failed to open the source pcap file: %s\n", errbuf); goto error; } /* link layer in the source dump must be Ethernet */ link_layer_type = pcap_datalink(handle); if(link_layer_type != DLT_EN10MB && link_layer_type != DLT_LINUX_SLL && link_layer_type != DLT_RAW) { fprintf(stderr, "link layer type %d not supported in source dump (supported = " "%d, %d, %d)\n", link_layer_type, DLT_EN10MB, DLT_LINUX_SLL, DLT_RAW); goto close_input; } if(link_layer_type == DLT_EN10MB) { link_len = ETHER_HDR_LEN; } else if(link_layer_type == DLT_LINUX_SLL) { link_len = LINUX_COOKED_HDR_LEN; } else /* DLT_RAW */ { link_len = 0; } /* create the decompressor */ decomp = rohc_decomp_new2(ROHC_SMALL_CID, ROHC_SMALL_CID_MAX, ROHC_O_MODE); if(decomp == NULL) { fprintf(stderr, "cannot create the decompressor\n"); goto close_input; } /* set the callback for traces */ if(!rohc_decomp_set_traces_cb2(decomp, print_rohc_traces, NULL)) { fprintf(stderr, "failed to set trace callback\n"); goto destroy_decomp; } /* enable decompression profiles */ if(!rohc_decomp_enable_profiles(decomp, ROHC_PROFILE_UNCOMPRESSED, ROHC_PROFILE_UDP, ROHC_PROFILE_IP, ROHC_PROFILE_UDPLITE, ROHC_PROFILE_RTP, ROHC_PROFILE_ESP, ROHC_PROFILE_TCP, -1)) { fprintf(stderr, "failed to enable the decompression profiles\n"); goto destroy_decomp; } /* for each packet in the dump */ counter = 0; while((packet = (unsigned char *) pcap_next(handle, &header)) != NULL) { const struct rohc_ts arrival_time = { .sec = 0, .nsec = 0 }; struct rohc_buf rohc_packet = rohc_buf_init_full(packet, header.caplen, arrival_time); uint8_t ip_buffer[MAX_ROHC_SIZE]; struct rohc_buf ip_packet = rohc_buf_init_empty(ip_buffer, MAX_ROHC_SIZE); uint8_t rcvd_feedback_buf[6]; struct rohc_buf rcvd_feedback = rohc_buf_init_empty(rcvd_feedback_buf, 6); uint8_t send_feedback_buf[6]; struct rohc_buf send_feedback = rohc_buf_init_empty(send_feedback_buf, 6); rohc_status_t status; counter++; /* check Ethernet frame length */ if(header.len < link_len || header.len != header.caplen) { fprintf(stderr, "bad PCAP packet (len = %d, caplen = %d)\n", header.len, header.caplen); goto destroy_decomp; } /* skip the link layer header */ rohc_buf_pull(&rohc_packet, link_len); fprintf(stderr, "decompress malformed packet #%u:\n", counter); /* decompress the ROHC packet */ status = rohc_decompress3(decomp, rohc_packet, &ip_packet, &rcvd_feedback, &send_feedback); fprintf(stderr, "\tdecompression status: %s\n", rohc_strerror(status)); if(status == ROHC_STATUS_OK) { if(counter >= failure_start) { fprintf(stderr, "\tunexpected successful decompression\n"); goto destroy_decomp; } else { fprintf(stderr, "\texpected successful decompression\n"); } } else { if(counter >= failure_start) { fprintf(stderr, "\texpected decompression failure\n"); } else { fprintf(stderr, "\tunexpected decompression failure\n"); goto destroy_decomp; } } /* be ready to get the next feedback to send */ rohc_buf_reset(&send_feedback); } is_failure = 0; destroy_decomp: rohc_decomp_free(decomp); close_input: pcap_close(handle); error: return is_failure; } /** * @brief Callback to print traces of the ROHC library * * @param priv_ctxt An optional private context, may be NULL * @param level The priority level of the trace * @param entity The entity that emitted the trace among: * \li ROHC_TRACE_COMP * \li ROHC_TRACE_DECOMP * @param profile The ID of the ROHC compression/decompression profile * the trace is related to * @param format The format string of the trace */ static void print_rohc_traces(void *const priv_ctxt, const rohc_trace_level_t level, const rohc_trace_entity_t entity, const int profile, const char *const format, ...) { const char *level_descrs[] = { [ROHC_TRACE_DEBUG] = "DEBUG", [ROHC_TRACE_INFO] = "INFO", [ROHC_TRACE_WARNING] = "WARNING", [ROHC_TRACE_ERROR] = "ERROR" }; if(level >= ROHC_TRACE_WARNING || is_verbose) { va_list args; fprintf(stdout, "[%s] ", level_descrs[level]); va_start(args, format); vfprintf(stdout, format, args); va_end(args); } }
/** * @brief Decompress one ROHC packet and compare the result with a reference * * @param decomp The decompressor to use to decompress the ROHC packet * @param num_packet A number affected to the IP packet to compress/decompress * @param header The PCAP header for the packet * @param packet The packet to compress/decompress (link layer included) * @param link_len_src The length of the link layer header before IP data * @param cmp_packet The ROHC packet for comparison purpose * @param cmp_size The size of the ROHC packet used for comparison * purpose * @param link_len_cmp The length of the link layer header before ROHC data * @return 0 if the process is successful * -1 if ROHC packet is malformed * -2 if decompression fails * -3 if the decompressed packet doesn't match the * reference */ static int test_decomp_one(struct rohc_decomp *const decomp, const size_t num_packet, const struct pcap_pkthdr header, const unsigned char *const packet, const size_t link_len_src, const unsigned char *const cmp_packet, const size_t cmp_size, const size_t link_len_cmp) { const struct rohc_ts arrival_time = { .sec = 0, .nsec = 0 }; struct rohc_buf rohc_packet = rohc_buf_init_full((uint8_t *) packet, header.caplen, arrival_time); uint8_t uncomp_buffer[MAX_ROHC_SIZE]; struct rohc_buf uncomp_packet = rohc_buf_init_empty(uncomp_buffer, MAX_ROHC_SIZE); rohc_status_t status; printf("=== decompressor packet #%zu:\n", num_packet); /* check Ethernet frame length */ if(header.len <= link_len_src || header.len != header.caplen) { fprintf(stderr, "bad PCAP packet (len = %u, caplen = %u)\n", header.len, header.caplen); goto error_fmt; } if(cmp_packet != NULL && cmp_size <= link_len_cmp) { fprintf(stderr, "bad comparison packet: too small for link header\n"); goto error_fmt; } /* skip the link layer header */ rohc_buf_pull(&rohc_packet, link_len_src); /* decompress the ROHC packet */ printf("=== ROHC decompression: start\n"); status = rohc_decompress3(decomp, rohc_packet, &uncomp_packet, NULL, NULL); if(status != ROHC_STATUS_OK) { size_t i; printf("=== ROHC decompression: failure\n"); printf("=== original %zu-byte compressed packet:\n", rohc_packet.len); for(i = 0; i < rohc_packet.len; i++) { if(i > 0 && (i % 16) == 0) { printf("\n"); } else if(i > 0 && (i % 8) == 0) { printf(" "); } printf("%02x ", rohc_buf_byte_at(rohc_packet, i)); } printf("\n\n"); goto error_decomp; } printf("=== ROHC decompression: success\n"); /* compare the decompressed packet with the original one */ printf("=== uncompressed packet comparison: start\n"); if(cmp_packet && !compare_packets(rohc_buf_data(uncomp_packet), uncomp_packet.len, cmp_packet + link_len_cmp, cmp_size - link_len_cmp)) { printf("=== uncompressed packet comparison: failure\n"); printf("\n"); goto error_cmp; } printf("=== uncompressed packet comparison: success\n"); printf("\n"); return 0; error_fmt: return -1; error_decomp: return -2; error_cmp: return -3; } /** * @brief Test the ROHC library decompression with a flow of ROHC packets * generated by another ROHC implementation * * @param cid_type The type of CIDs the compressor shall use * @param wlsb_width The width of the WLSB window to use * @param max_contexts The maximum number of ROHC contexts to use * @param src_filename The name of the PCAP file that contains the * ROHC packets to decompress * @param cmp_filename The name of the PCAP file that contains the * uncompressed packets used for comparison * @return 0 in case of success, * 1 in case of failure, * 77 if test is skipped */ static int test_decomp_all(const rohc_cid_type_t cid_type, const size_t wlsb_width, const size_t max_contexts, const char *const src_filename, const char *const cmp_filename) { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *handle; struct pcap_pkthdr header; int link_layer_type_src; size_t link_len_src; unsigned char *packet; pcap_t *cmp_handle; struct pcap_pkthdr cmp_header; int link_layer_type_cmp; size_t link_len_cmp = 0; unsigned char *cmp_packet; size_t counter; struct rohc_decomp *decomp; size_t nb_bad = 0; size_t nb_ok = 0; size_t err_decomp = 0; size_t nb_ref = 0; int status = 1; int ret; printf("=== initialization:\n"); /* open the source dump file */ handle = pcap_open_offline(src_filename, errbuf); if(handle == NULL) { printf("failed to open the source pcap file: %s\n", errbuf); goto error; } /* link layer in the source dump must be Ethernet */ link_layer_type_src = pcap_datalink(handle); if(link_layer_type_src != DLT_EN10MB && link_layer_type_src != DLT_LINUX_SLL && link_layer_type_src != DLT_RAW && link_layer_type_src != DLT_NULL) { printf("link layer type %d not supported in source dump (supported = " "%d, %d, %d, %d)\n", link_layer_type_src, DLT_EN10MB, DLT_LINUX_SLL, DLT_RAW, DLT_NULL); status = 77; /* skip test */ goto close_input; } if(link_layer_type_src == DLT_EN10MB) { link_len_src = ETHER_HDR_LEN; } else if(link_layer_type_src == DLT_LINUX_SLL) { link_len_src = LINUX_COOKED_HDR_LEN; } else if(link_layer_type_src == DLT_NULL) { link_len_src = BSD_LOOPBACK_HDR_LEN; } else /* DLT_RAW */ { link_len_src = 0; } /* open the uncompressed comparison dump file if asked */ if(cmp_filename != NULL) { cmp_handle = pcap_open_offline(cmp_filename, errbuf); if(cmp_handle == NULL) { printf("failed to open the comparison pcap file: %s\n", errbuf); goto close_input; } /* link layer in the rohc_comparison dump must be Ethernet */ link_layer_type_cmp = pcap_datalink(cmp_handle); if(link_layer_type_cmp != DLT_EN10MB && link_layer_type_cmp != DLT_LINUX_SLL && link_layer_type_cmp != DLT_RAW && link_layer_type_cmp != DLT_NULL) { printf("link layer type %d not supported in comparision dump " "(supported = %d, %d, %d, %d)\n", link_layer_type_cmp, DLT_EN10MB, DLT_LINUX_SLL, DLT_RAW, DLT_NULL); status = 77; /* skip test */ goto close_comparison; } if(link_layer_type_cmp == DLT_EN10MB) { link_len_cmp = ETHER_HDR_LEN; } else if(link_layer_type_cmp == DLT_LINUX_SLL) { link_len_cmp = LINUX_COOKED_HDR_LEN; } else if(link_layer_type_cmp == DLT_NULL) { link_len_cmp = BSD_LOOPBACK_HDR_LEN; } else /* DLT_RAW */ { link_len_cmp = 0; } } else { cmp_handle = NULL; } /* create the decompressor */ decomp = create_decompressor(cid_type, max_contexts); if(decomp == NULL) { printf("failed to create the decompressor 1\n"); goto close_comparison; } printf("\n"); /* for each ROHC packet in the dump */ counter = 0; while((packet = (unsigned char *) pcap_next(handle, &header)) != NULL) { counter++; /* get next uncompressed packet from the comparison dump file if asked */ if(cmp_handle != NULL) { cmp_packet = (unsigned char *) pcap_next(cmp_handle, &cmp_header); } else { cmp_packet = NULL; cmp_header.caplen = 0; } /* decompress one packet of the flow and compare it with the given * reference */ ret = test_decomp_one(decomp, counter, header, packet, link_len_src, cmp_packet, cmp_header.caplen, link_len_cmp); if(ret == 0) { nb_ok++; } else if(ret == -2) { err_decomp++; } else if(ret != -3) { nb_ref++; } else { nb_bad++; } } /* show the compression/decompression results */ printf("=== summary:\n"); printf("===\tpackets_processed: %zu\n", counter); printf("===\tmalformed: %zu\n", nb_bad); printf("===\tdecompression_failed: %zu\n", err_decomp); printf("===\tmatches: %zu\n", nb_ok); printf("\n"); /* show some info/stats about the decompressor */ if(!show_rohc_decomp_stats(decomp)) { fprintf(stderr, "failed to dump ROHC decompressor stats\n"); goto free_decomp; } printf("\n"); /* destroy the compressors and decompressors */ printf("=== shutdown:\n"); if(err_decomp == 0 && nb_bad == 0 && nb_ref == 0 && nb_ok == counter) { /* test is successful */ status = 0; } free_decomp: rohc_decomp_free(decomp); close_comparison: if(cmp_handle != NULL) { pcap_close(cmp_handle); } close_input: pcap_close(handle); error: return status; }