/** * @brief Check whether the CRC on uncompressed header is correct or not * * @param context The decompression context * @param uncomp_hdrs The uncompressed headers * @param crc_pkt The CRC over uncompressed headers extracted from packet * @return true if the CRC is correct, false otherwise */ bool rohc_decomp_check_uncomp_crc(const struct rohc_decomp_ctxt *const context, struct rohc_buf *const uncomp_hdrs, const struct rohc_decomp_crc_one *const crc_pkt) { uint8_t crc_computed; /* determine the initial value and the pre-computed table for the CRC */ switch(crc_pkt->type) { case ROHC_CRC_TYPE_3: crc_computed = CRC_INIT_3; break; case ROHC_CRC_TYPE_7: crc_computed = CRC_INIT_7; break; case ROHC_CRC_TYPE_8: rohc_decomp_warn(context, "unexpected CRC type %d", crc_pkt->type); assert(0); goto error; case ROHC_CRC_TYPE_NONE: default: rohc_decomp_warn(context, "unknown CRC type %d", crc_pkt->type); assert(0); goto error; } /* compute the CRC from built uncompressed headers */ crc_computed = crc_calculate(crc_pkt->type, rohc_buf_data(*uncomp_hdrs), uncomp_hdrs->len, crc_computed); rohc_decomp_debug(context, "CRC-%d on uncompressed header = 0x%x", crc_pkt->type, crc_computed); /* does the computed CRC match the one in packet? */ if(crc_computed != crc_pkt->bits) { rohc_decomp_warn(context, "CRC failure (computed = 0x%02x, packet = " "0x%02x)", crc_computed, crc_pkt->bits); goto error; } /* computed CRC matches the one in packet */ return true; error: return false; }
/** * @brief Parse a network packet * * @param[out] packet The parsed packet * @param data The data to parse * @param trace_cb The function to call for printing traces * @param trace_cb_priv An optional private context, may be NULL * @param trace_entity The entity that emits the traces * @return true if the packet was successfully parsed, * false if a problem occurred (a malformed packet is * not considered as an error) */ bool net_pkt_parse(struct net_pkt *const packet, const struct rohc_buf data, rohc_trace_callback2_t trace_cb, void *const trace_cb_priv, rohc_trace_entity_t trace_entity) { packet->data = rohc_buf_data(data); packet->len = data.len; packet->ip_hdr_nr = 0; packet->key = 0; /* traces */ packet->trace_callback = trace_cb; packet->trace_callback_priv = trace_cb_priv; /* create the outer IP packet from raw data */ if(!ip_create(&packet->outer_ip, rohc_buf_data(data), data.len)) { rohc_warning(packet, trace_entity, ROHC_PROFILE_GENERAL, "cannot create the outer IP header"); goto error; } packet->ip_hdr_nr++; rohc_debug(packet, trace_entity, ROHC_PROFILE_GENERAL, "outer IP header: %u bytes", ip_get_totlen(&packet->outer_ip)); rohc_debug(packet, trace_entity, ROHC_PROFILE_GENERAL, "outer IP header: version %d", ip_get_version(&packet->outer_ip)); if(packet->outer_ip.nh.data != NULL) { rohc_debug(packet, trace_entity, ROHC_PROFILE_GENERAL, "outer IP header: next header is of type %d", packet->outer_ip.nh.proto); if(packet->outer_ip.nl.data != NULL) { rohc_debug(packet, trace_entity, ROHC_PROFILE_GENERAL, "outer IP header: next layer is of type %d", packet->outer_ip.nl.proto); } } /* build the hash key for the packet */ if(ip_get_version(&packet->outer_ip) == IPV4) { packet->key ^= ipv4_get_saddr(&packet->outer_ip); packet->key ^= ipv4_get_daddr(&packet->outer_ip); } else if(ip_get_version(&packet->outer_ip) == IPV6) { const struct ipv6_addr *const saddr = ipv6_get_saddr(&packet->outer_ip); const struct ipv6_addr *const daddr = ipv6_get_daddr(&packet->outer_ip); packet->key ^= saddr->addr.u32[0]; packet->key ^= saddr->addr.u32[1]; packet->key ^= saddr->addr.u32[2]; packet->key ^= saddr->addr.u32[3]; packet->key ^= daddr->addr.u32[0]; packet->key ^= daddr->addr.u32[1]; packet->key ^= daddr->addr.u32[2]; packet->key ^= daddr->addr.u32[3]; } /* get the transport protocol */ packet->transport = &packet->outer_ip.nl; /* is there any inner IP header? */ if(rohc_is_tunneling(packet->transport->proto)) { /* create the second IP header */ if(!ip_get_inner_packet(&packet->outer_ip, &packet->inner_ip)) { rohc_warning(packet, trace_entity, ROHC_PROFILE_GENERAL, "cannot create the inner IP header"); goto error; } packet->ip_hdr_nr++; rohc_debug(packet, trace_entity, ROHC_PROFILE_GENERAL, "inner IP header: %u bytes", ip_get_totlen(&packet->inner_ip)); rohc_debug(packet, trace_entity, ROHC_PROFILE_GENERAL, "inner IP header: version %d", ip_get_version(&packet->inner_ip)); if(packet->inner_ip.nh.data != NULL) { rohc_debug(packet, trace_entity, ROHC_PROFILE_GENERAL, "inner IP header: next header is of type %d", packet->inner_ip.nh.proto); if(packet->inner_ip.nl.data != NULL) { rohc_debug(packet, trace_entity, ROHC_PROFILE_GENERAL, "inner IP header: next layer is of type %d", packet->inner_ip.nl.proto); } } /* get the transport protocol */ packet->transport = &packet->inner_ip.nl; } return true; error: return false; }
/** * @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; }