/** * @brief Get the XI information, ie. the X bit and the index value * * @param ps The PS bit * @param xi_index The index of the XI * @param xi_1 The XI 1 field if PS = 1 (4-bit XI) * @param data The data to parse * @param[out] is_item_present Whether the XI item shall be present or not * @return The value of the XI index */ static uint8_t rohc_list_get_xi_type_2or3(const int ps, const size_t xi_index, const uint8_t xi_1, const uint8_t *const data, bool *const is_item_present) { uint8_t xi_value; /* extract the value of the XI index */ if(ps == 1) { /* ROHC header contains 8-bit XIs */ *is_item_present = GET_BOOL(GET_BIT_7(data + xi_index)); xi_value = GET_BIT_0_6(data + xi_index); } else { /* ROHC header contains 4-bit XIs: encoding type 2 and 3 store XI #1 * in their first byte, so next XIs are shifted by 4 bits */ /* which type of XI do we parse ? first one, odd one or even one ? */ if(xi_index == 0) { /* first XI is stored in the first byte of the header */ *is_item_present = GET_BOOL(GET_BIT_3(&xi_1)); xi_value = GET_BIT_0_2(&xi_1); } else if((xi_index % 2) != 0) { /* handle odd XI, ie. XI stored in MSB */ *is_item_present = GET_BOOL(GET_BIT_7(data + (xi_index - 1) / 2)); xi_value = GET_BIT_4_6(data + (xi_index - 1) / 2); } else { /* handle even XI, ie. XI stored in LSB */ *is_item_present = GET_BOOL(GET_BIT_3(data + (xi_index - 1) / 2)); xi_value = GET_BIT_0_2(data + (xi_index - 1) / 2); } } return xi_value; }
/** * @brief Determine the number of bits set to 1 in the insertion/removal bit mask * * @param decomp The list decompressor * @param descr The name of bit mask being decoded * @param packet The ROHC packet to decompress * @param packet_len The length (in bytes) of the packet to decompress * @param[out] mask The insertion/removal bit mask on 1-2 bytes * @param[out] mask_len The length of the insertion/removal mask (in bits) * @return \li In case of success, the number of bytes read in the * given packet, ie. the length of the compressed list * \li -1 in case of failure */ static int rohc_list_decode_mask(struct list_decomp *const decomp, const char *const descr, const uint8_t *const packet, const size_t packet_len, uint8_t mask[2], size_t *const mask_len) { size_t parsed_bytes_nr; /* is there enough data in packet for minimal insertion bit mask field ? */ if(packet_len < 1) { rd_list_warn(decomp, "packet too small for minimal %s bit mask field " "(only %zu bytes while at least 1 byte is required)", descr, packet_len); goto error; } /* determine the number of bits set to 1 in the insertion bit mask */ mask[0] = packet[0]; rd_list_debug(decomp, "%s bit mask (first byte) = 0x%02x", descr, mask[0]); if(GET_REAL(GET_BIT_7(mask)) == 1) { /* 15-bit mask */ if(packet_len < 2) { rd_list_warn(decomp, "packet too small for a 2-byte %s bit mask " "(only %zu bytes available)", descr, packet_len); goto error; } *mask_len = 15; mask[1] = packet[1]; rd_list_debug(decomp, "%s bit mask (second byte) = 0x%02x", descr, mask[1]); parsed_bytes_nr = 2; } else { /* 7-bit mask */ rd_list_debug(decomp, "no second byte of %s bit mask", descr); *mask_len = 7; mask[1] = 0; /* shut up compiler warning about read of uninitialized data */ parsed_bytes_nr = 1; } return parsed_bytes_nr; error: return -1; }
/** * @brief Get the XI information, ie. the X bit and the index value * * @param ps The PS bit * @param xi_index The index of the XI * @param data The data to parse * @param[out] is_item_present Whether the XI item shall be present or not * @return The value of the XI index */ static uint8_t rohc_list_get_xi_type_0(const int ps, const size_t xi_index, const uint8_t *const data, bool *const is_item_present) { uint8_t xi_value; /* extract the value of the XI index */ if(ps == 1) { /* ROHC header contains 8-bit XIs */ *is_item_present = GET_BOOL(GET_BIT_7(data + xi_index)); xi_value = GET_BIT_0_6(data + xi_index); } else { /* ROHC header contains 4-bit XIs: encoding type 0 stores XI #1 * with all other XIs */ /* which type of XI do we parse ? even or odd one ? */ if((xi_index % 2) == 0) { /* handle even XI, ie. XI stored in MSB */ *is_item_present = GET_BOOL(GET_BIT_7(data + xi_index / 2)); xi_value = GET_BIT_4_6(data + xi_index / 2); } else { /* handle odd XI, ie. XI stored in LSB */ *is_item_present = GET_BOOL(GET_BIT_3(data + xi_index / 2)); xi_value = GET_BIT_0_2(data + xi_index / 2); } } return xi_value; }
/** * @brief Get the bit in the given byte at the given position * * @param byte The byte to analyse * @param pos The position between 0 and 7 * @return The requested bit */ static uint8_t rohc_get_bit(const unsigned char byte, const size_t pos) { uint8_t bit; switch(pos) { case 0: bit = GET_REAL(GET_BIT_0(&byte)); break; case 1: bit = GET_REAL(GET_BIT_1(&byte)); break; case 2: bit = GET_REAL(GET_BIT_2(&byte)); break; case 3: bit = GET_REAL(GET_BIT_3(&byte)); break; case 4: bit = GET_REAL(GET_BIT_4(&byte)); break; case 5: bit = GET_REAL(GET_BIT_5(&byte)); break; case 6: bit = GET_REAL(GET_BIT_6(&byte)); break; case 7: bit = GET_REAL(GET_BIT_7(&byte)); break; default: /* there is no such bit in a byte */ assert(0); /* should not happen */ bit = 0; break; } return bit; }
/** * @brief Process the insertion scheme of the list compression * * @param decomp The list decompressor * @param packet The ROHC packet to decompress * @param packet_len The length (in bytes) of the packet to decompress * @param ps The PS bit * @param xi_1 The XI 1 field if PS = 1 (4-bit XI) * @param items_nr The number of items in the initial list * @param ref_list The list to use as reference * @param[out] ins_list The list with new items added * @return \li In case of success, the number of bytes read in the * given packet, ie. the length of the insertion mask * \li -1 in case of failure */ static int rohc_list_parse_insertion_scheme(struct list_decomp *const decomp, const uint8_t *packet, size_t packet_len, const int ps, const int xi_1, const size_t items_nr, const struct rohc_list *const ref_list, struct rohc_list *const ins_list) { size_t packet_read_len = 0; uint8_t mask[2]; /* removal bit mask on 1-2 bytes */ size_t mask_len; /* length (in bits) of the removal mask */ size_t item_read_len; /* the amount of bytes currently read in the item field */ size_t ref_list_cur_pos; /* current position in reference list */ size_t xi_index; /* the index of the current XI in XI list */ size_t xi_len; /* the length (in bytes) of the XI list */ size_t k; /* the number of ones in insertion mask and the number of elements in XI list */ size_t i; int ret; assert(ps == 0 || ps == 1); /* parse the insertion bit mask */ ret = rohc_list_decode_mask(decomp, "insertion", packet, packet_len, mask, &mask_len); if(ret < 0) { rd_list_warn(decomp, "failed to parse the insertion bit mask"); goto error; } packet += ret; packet_read_len += ret; packet_len -= ret; /* determine the number of indexes in the XI list */ k = rohc_list_get_xi_nr(mask, mask_len); /* determine the length (in bytes) of the XI list */ xi_len = rohc_list_get_xi_len(k, ps); if(packet_len < xi_len) { rd_list_warn(decomp, "packet too small for k = %zu XI items (only %zu " "bytes while at least %zu bytes are required)", k, packet_len, xi_len); goto error; } /* will the decompressed list contain too many items? */ if((items_nr + k) > ROHC_LIST_ITEMS_MAX) { rd_list_warn(decomp, "failed to decompress list based on reference list " "with %zu existing items and %zu additional new items: too " "many items for list (%u items max)", items_nr, k, ROHC_LIST_ITEMS_MAX); goto error; } /* create current list with reference list and new provided items */ xi_index = 0; item_read_len = 0; ref_list_cur_pos = 0; for(i = 0; i < mask_len; i++) { uint8_t new_item_to_insert; /* retrieve the corresponding bit in the insertion mask */ if(i < 7) { /* bit is located in first byte of insertion mask */ new_item_to_insert = rohc_get_bit(mask[0], 6 - i); } else { /* bit is located in 2nd byte of insertion mask */ new_item_to_insert = rohc_get_bit(mask[1], 14 - i); } /* insert item if required */ if(!new_item_to_insert) { /* take the next item from reference list (if there no more item in reference list, do nothing) */ if(ref_list_cur_pos < ref_list->items_nr) { /* new list, insert the item from reference list */ rd_list_debug(decomp, "use item from reference list (index %zu) into " "current list (index %zu)", ref_list_cur_pos, i); /* use next item from reference list */ ins_list->items[i] = ref_list->items[ref_list_cur_pos]; ins_list->items_nr++; /* skip item in removal list */ ref_list_cur_pos++; } } else { unsigned int xi_x_value; /* the value of the X field in one XI field */ unsigned int xi_index_value; /* the value of the Index field in one XI field */ /* new item to insert in list, parse the related XI field */ if(!ps) { /* ROHC header contains 4-bit XIs */ /* which type of XI do we parse ? first one, odd one or even one ? */ if(xi_index == 0) { /* first XI is stored in the first byte of the header */ xi_x_value = GET_BIT_3(&xi_1); xi_index_value = GET_BIT_0_2(&xi_1); } else if((xi_index % 2) != 0) { /* handle odd XI, ie. XI stored in MSB */ xi_x_value = GET_BIT_7(packet + (xi_index - 1) / 2); xi_index_value = GET_BIT_4_6(packet + (xi_index - 1) / 2); } else { /* handle even XI, ie. XI stored in LSB */ xi_x_value = GET_BIT_3(packet + (xi_index - 1) / 2); xi_index_value = GET_BIT_0_2(packet + (xi_index - 1) / 2); } } else { /* ROHC header contains 8-bit XIs */ xi_x_value = GET_BIT_7(packet + xi_index); xi_index_value = GET_BIT_0_6(packet + xi_index); } /* is the XI index valid? */ if(!decomp->check_item(decomp, xi_index_value)) { rd_list_warn(decomp, "XI #%zu got invalid index %u", xi_index, xi_index_value); goto error; } /* parse the corresponding item if present */ if(xi_x_value) { const uint8_t *const xi_item = packet + xi_len + item_read_len; const size_t xi_item_max_len = packet_len - xi_len - item_read_len; size_t item_len; rd_list_debug(decomp, "handle XI item #%zu", xi_index); /* create (or update if it already exists) the corresponding * item with the item transmitted in the ROHC header */ if(!rohc_decomp_list_create_item(decomp, xi_index, xi_index_value, xi_item, xi_item_max_len, &item_len)) { rd_list_warn(decomp, "failed to create XI item #%zu from " "packet", xi_index); goto error; } /* skip the item in ROHC header */ item_read_len += item_len; } else { /* X bit not set in XI, so item is not provided in ROHC header, * it must already be known by decompressor */ if(!decomp->trans_table[xi_index_value].known) { rd_list_warn(decomp, "list item with index #%u referenced by " "XI #%zu is not known yet", xi_index_value, xi_index); goto error; } } /* use new item from packet */ rd_list_debug(decomp, "use new item #%zu into current list (index " "%zu)", xi_index, i); ins_list->items[i] = &(decomp->trans_table[xi_index_value]); ins_list->items_nr++; /* skip the XI we have just parsed */ xi_index++; } } /* ensure that in case of an even number of 4-bit XIs, the 4 bits of * padding are set to 0 */ if(ps == 0 && (k % 2) == 0) { const uint8_t xi_padding = GET_BIT_0_3(packet + xi_len - 1); if(xi_padding != 0) { rd_list_warn(decomp, "sender does not conform to ROHC standards: " "when an even number of 4-bit XIs is used, the last 4 " "bits of the XI list should be set to 0, not 0x%x", xi_padding); #ifdef ROHC_RFC_STRICT_DECOMPRESSOR goto error; #endif } } /* skip the XI list and the item list */ packet_read_len += xi_len + item_read_len; #ifndef __clang_analyzer__ /* silent warning about dead in/decrement */ packet_len -= xi_len + item_read_len; #endif return packet_read_len; error: return -1; }
/** * @brief Decode an extension list type 0 * * @param decomp The list decompressor * @param packet The ROHC packet to decompress * @param packet_len The length (in bytes) of the packet to decompress * @param gen_id The id of the current list, * maybe ROHC_LIST_GEN_ID_ANON if not defined * @param ps The ps field * @param m The m field * @return \li In case of success, the number of bytes read in the given * packet, ie. the length of the compressed list * \li -1 in case of failure */ static int rohc_list_decode_type_0(struct list_decomp *const decomp, const unsigned char *packet, size_t packet_len, const unsigned int gen_id, const int ps, const uint8_t m) { size_t packet_read_len = 0; size_t xi_len; /* the length (in bytes) of the XI list */ unsigned int xi_index; /* the index of the current XI in XI list */ size_t item_read_len; /* the amount of bytes currently read in the item field */ assert(decomp != NULL); assert(packet != NULL); assert(gen_id != ROHC_LIST_GEN_ID_NONE); assert(ps == 0 || ps == 1); assert(m <= ROHC_LIST_ITEMS_MAX); /* determine the length (in bytes) of the XI list */ if(!ps) { /* 4-bit XIs */ if((m % 2) == 0) { /* even number of XI fields */ xi_len = m / 2; } else { /* odd number of XI fields, there are 4 bits of padding */ xi_len = (m + 1) / 2; } } else { /* 8-bit XIs */ xi_len = m; } /* is there enough room in packet for all the XI list ? */ if(packet_len < xi_len) { rd_list_warn(decomp, "packet too small for m = %d XI items (only %zu " "bytes while at least %zu bytes are required)", m, packet_len, xi_len); goto error; } /* creation of the list from the m XI items */ item_read_len = 0; for(xi_index = 0; xi_index < m; xi_index++) { unsigned int xi_x_value; /* the value of the X field in one XI field */ unsigned int xi_index_value; /* the value of the Index field in one XI field */ /* extract the value of the XI index */ if(!ps) { /* 4-bit XI */ if((xi_index % 2) == 0) { /* 4-bit XI is stored in MSB */ xi_x_value = GET_BIT_7(packet + xi_index / 2); xi_index_value = GET_BIT_4_6(packet + xi_index / 2); } else { /* 4-bit XI is stored in LSB */ xi_x_value = GET_BIT_3(packet + xi_index / 2); xi_index_value = GET_BIT_0_2(packet + xi_index / 2); } rd_list_debug(decomp, "0x%02x: XI #%u got index %u", packet[xi_index / 2], xi_index, xi_index_value); } else { /* 8-bit XI */ xi_x_value = GET_BIT_7(packet + xi_index); xi_index_value = GET_BIT_0_6(packet + xi_index); rd_list_debug(decomp, "0x%02x: XI #%u got index %u", packet[xi_index], xi_index, xi_index_value); } /* is the XI index valid? */ if(!decomp->check_item(decomp, xi_index_value)) { rd_list_warn(decomp, "XI #%u got invalid index %u", xi_index, xi_index_value); goto error; } /* is there a corresponding item in packet after the XI list? */ if(xi_x_value) { const uint8_t *const xi_item = packet + xi_len + item_read_len; const size_t xi_item_max_len = packet_len - xi_len - item_read_len; size_t item_len; rd_list_debug(decomp, "handle XI item #%u", xi_index); /* create (or update if it already exists) the corresponding item * with the item transmitted in the ROHC header */ if(!rohc_decomp_list_create_item(decomp, xi_index, xi_index_value, xi_item, xi_item_max_len, &item_len)) { rd_list_warn(decomp, "failed to create XI item #%u from packet", xi_index); goto error; } /* skip the item in ROHC header */ item_read_len += item_len; } else { /* X bit not set in XI, so item is not provided in ROHC header, * it must already be known by decompressor */ if(!decomp->trans_table[xi_index_value].known) { rd_list_warn(decomp, "list item with index #%u referenced by XI " "#%d is not known yet", xi_index_value, xi_index); goto error; } } /* record the structure of the list of the current packet */ assert(decomp->pkt_list.items_nr < ROHC_LIST_ITEMS_MAX); decomp->pkt_list.items[decomp->pkt_list.items_nr] = &decomp->trans_table[xi_index_value]; decomp->pkt_list.items_nr++; rd_list_debug(decomp, " XI #%u: use item of type 0x%02x (index %u in " "translation table) in list", xi_index, decomp->trans_table[xi_index_value].type, xi_index_value); } /* ensure that in case of an odd number of 4-bit XIs, the 4 bits of padding are set to 0 */ if(ps == 0 && (m % 2) != 0) { const uint8_t xi_padding = GET_BIT_0_3(packet + xi_len - 1); if(xi_padding != 0) { rd_list_warn(decomp, "sender does not conform to ROHC standards: " "when an odd number of 4-bit XIs is used, the last 4 " "bits of the XI list should be set to 0, not 0x%x", xi_padding); #ifdef ROHC_RFC_STRICT_DECOMPRESSOR goto error; #endif } } /* skip the XI list and the item list */ packet_read_len += xi_len + item_read_len; #ifndef __clang_analyzer__ /* silent warning about dead in/decrement */ packet_len -= xi_len + item_read_len; #endif return packet_read_len; error: return -1; }
/** * @brief Decode the static IP header of the rohc packet. * * @param context The decompression context * @param rohc_packet The remaining part of the ROHC packet * @param rohc_length The remaining length (in bytes) of the ROHC packet * @param[out] ip_bits The bits extracted from the IP part of the static chain * @param[out] nh_proto The next header protocol of the last extension header * @return The length of static IP header in case of success, * -1 if an error occurs */ static int tcp_parse_static_ip(const struct rohc_decomp_ctxt *const context, const uint8_t *const rohc_packet, const size_t rohc_length, struct rohc_tcp_extr_ip_bits *const ip_bits, uint8_t *const nh_proto) { const uint8_t *remain_data = rohc_packet; size_t remain_len = rohc_length; size_t read = 0; int ret; rohc_decomp_debug(context, "parse IP static part"); /* at least 1 byte required to read the version flag */ if(remain_len < 1) { rohc_decomp_warn(context, "malformed ROHC packet: too short for the " "version flag of the IP static part"); goto error; } /* parse IPv4 static part or IPv6 static part? */ if(GET_BIT_7(remain_data) == 0) { const ipv4_static_t *const ipv4_static = (ipv4_static_t *) remain_data; rohc_decomp_debug(context, " IPv4 static part"); ip_bits->version = IPV4; if(remain_len < sizeof(ipv4_static_t)) { rohc_decomp_warn(context, "malformed ROHC packet: too short for the " "IPv4 static part"); goto error; } ip_bits->proto = ipv4_static->protocol; ip_bits->proto_nr = 8; *nh_proto = ip_bits->proto; memcpy(ip_bits->saddr, &ipv4_static->src_addr, sizeof(uint32_t)); ip_bits->saddr_nr = 32; memcpy(ip_bits->daddr, &ipv4_static->dst_addr, sizeof(uint32_t)); ip_bits->daddr_nr = 32; /* IP extension headers not supported for IPv4 */ ip_bits->opts_nr = 0; ip_bits->opts_len = 0; read += sizeof(ipv4_static_t); #ifndef __clang_analyzer__ /* silent warning about dead in/decrement */ remain_data += sizeof(ipv4_static_t); remain_len -= sizeof(ipv4_static_t); #endif } else { rohc_decomp_debug(context, " IPv6 static part"); ip_bits->version = IPV6; /* static 1 or static 2 variant? */ if(GET_BIT_4(remain_data) == 0) { const ipv6_static1_t *const ipv6_static1 = (ipv6_static1_t *) remain_data; if(remain_len < sizeof(ipv6_static1_t)) { rohc_decomp_warn(context, "malformed ROHC packet: too short for " "the IPv6 static part"); goto error; } ip_bits->flowid = 0; ip_bits->flowid_nr = 20; ip_bits->proto = ipv6_static1->next_header; ip_bits->proto_nr = 8; memcpy(ip_bits->saddr, &ipv6_static1->src_addr, sizeof(uint32_t) * 4); ip_bits->saddr_nr = 128; memcpy(ip_bits->daddr, &ipv6_static1->dst_addr, sizeof(uint32_t) * 4); ip_bits->daddr_nr = 128; read += sizeof(ipv6_static1_t); remain_data += sizeof(ipv6_static1_t); remain_len -= sizeof(ipv6_static1_t); } else { const ipv6_static2_t *const ipv6_static2 = (ipv6_static2_t *) remain_data; if(remain_len < sizeof(ipv6_static2_t)) { rohc_decomp_warn(context, "malformed ROHC packet: too short for " "the IPv6 static part"); goto error; } ip_bits->flowid = (ipv6_static2->flow_label1 << 16) | rohc_ntoh16(ipv6_static2->flow_label2); assert((ip_bits->flowid & 0xfffff) == ip_bits->flowid); rohc_decomp_debug(context, " IPv6 flow label = 0x%05x", ip_bits->flowid); ip_bits->flowid_nr = 20; ip_bits->proto = ipv6_static2->next_header; ip_bits->proto_nr = 8; memcpy(ip_bits->saddr, &ipv6_static2->src_addr, sizeof(uint32_t) * 4); ip_bits->saddr_nr = 128; memcpy(ip_bits->daddr, &ipv6_static2->dst_addr, sizeof(uint32_t) * 4); ip_bits->daddr_nr = 128; read += sizeof(ipv6_static2_t); remain_data += sizeof(ipv6_static2_t); remain_len -= sizeof(ipv6_static2_t); } *nh_proto = ip_bits->proto; ip_bits->opts_nr = 0; ip_bits->opts_len = 0; while(rohc_is_ipv6_opt(*nh_proto)) { ip_option_context_t *opt; if(ip_bits->opts_nr >= ROHC_TCP_MAX_IP_EXT_HDRS) { rohc_decomp_warn(context, "too many IPv6 extension headers"); goto error; } opt = &(ip_bits->opts[ip_bits->opts_nr]); ret = tcp_parse_static_ipv6_option(context, ip_bits, opt, *nh_proto, remain_data, remain_len); if(ret < 0) { rohc_decomp_warn(context, "malformed ROHC packet: malformed " "IPv6 static option part"); goto error; } rohc_decomp_debug(context, "IPv6 static option part is %d-byte length", ret); assert(remain_len >= ((size_t) ret)); read += ret; remain_data += ret; remain_len -= ret; *nh_proto = opt->nh_proto; ip_bits->opts_nr++; } rohc_decomp_debug(context, "IPv6 header is followed by %zu extension " "headers", ip_bits->opts_nr); } rohc_decomp_dump_buf(context, "IP static part", rohc_packet, read); return read; error: return -1; }