static int extmap_find(const void *key, const void *ele) { const uint8_t *k = key, *e = ele; if (*k < *e) return -1; if (*k > *e) return 1; return 0; } static bool extmap_has_ext(uint8_t id, struct rtpengine_output *o) { uint8_t const *match = bsearch(&id, o->output.extmap_filter, o->output.num_extmap_filter, sizeof(*o->output.extmap_filter), extmap_find); return match != NULL; } static bool apply_extmap_filter_ext(uint8_t id, size_t len, size_t size, unsigned char **r, unsigned char **w, unsigned char *end, unsigned int *count, struct rtpengine_output *o) { if (*r + size + len > end) return false; if (extmap_has_ext(id, o)) { // retain ext if (*r != *w) memmove(*w, *r, len + size); *w += len + size; (*count)++; } // else: skip over & filter out *r += len + size; return true; } static unsigned char *insert_skb_data(unsigned char **w, struct sk_buff *skb, struct rtp_parsed *rtp, unsigned int len) { unsigned char *ret = *w; unsigned char *end = ret + len; if (end > rtp->payload) { size_t put = end - rtp->payload; skb_put(skb, put); memmove(rtp->payload + put, rtp->payload, rtp->payload_len); rtp->payload += put; } (*w) += len; return ret; } static void add_extmap_ext_short(unsigned char **w, unsigned int *count, struct sk_buff *skb, struct rtp_parsed *rtp, uint8_t id, uint8_t len, char *content) { unsigned char *wp = insert_skb_data(w, skb, rtp, 1 + len); *wp++ = (id << 4) | ((len - 1) & 0xf); memcpy(wp, content, len); (*count)++; } static void add_extmap_ext_long(unsigned char **w, unsigned int *count, struct sk_buff *skb, struct rtp_parsed *rtp, uint8_t id, uint8_t len, char *content) { unsigned char *wp = insert_skb_data(w, skb, rtp, 2 + len); *wp++ = id; *wp++ = len; memcpy(wp, content, len); (*count)++; } static void add_extmap_exts_short(unsigned char **w, unsigned int *count, struct rtpengine_output *o, struct rtp_parsed *rtp, struct sk_buff *skb) { if (!o->output.extmap_mid) return; add_extmap_ext_short(w, count, skb, rtp, o->output.extmap_mid, o->output.extmap_mid_len, o->output.extmap_mid_str); } static void add_extmap_exts_long(unsigned char **w, unsigned int *count, struct rtpengine_output *o, struct rtp_parsed *rtp, struct sk_buff *skb) { if (!o->output.extmap_mid) return; add_extmap_ext_long(w, count, skb, rtp, o->output.extmap_mid, o->output.extmap_mid_len, o->output.extmap_mid_str); } static void apply_extmap_filter_finish(unsigned char *r, unsigned char *w, unsigned int count, struct rtpengine_output *o, struct sk_buff *skb, struct rtp_parsed *rtp) { size_t size; size_t padded; if (r == w) return; // everything left as it was if (count == 0) { size_t pull; // no extensions, remove header and trim packet rtp->rtp_header->v_p_x_cc &= ~0x10; pull = rtp->payload - (unsigned char *) rtp->ext_hdr; memmove(rtp->ext_hdr, rtp->payload, rtp->payload_len); rtp->payload = (unsigned char *) rtp->ext_hdr; rtp->ext_hdr = NULL; rtp->extension_len = 0; skb_trim(skb, skb->len - pull); return; } // shift payload and adjust packet length rtp->rtp_header->v_p_x_cc |= 0x90; size = w - rtp->extension; padded = (size + 3L) & ~3L; rtp->ext_hdr->length = htons(padded / 4); if (rtp->extension_len >= padded) { size_t pull; // shift up payload and trim packet memset(w, 0, padded - size); pull = rtp->extension_len - padded; rtp->extension_len = padded; memmove(rtp->payload - pull, rtp->payload, rtp->payload_len); rtp->payload -= pull; skb_trim(skb, skb->len - pull); } else { // shift down payload and extend packet for padding size_t put = padded - size; unsigned char *wp = insert_skb_data(&w, skb, rtp, put); memset(wp, 0, put); } } static void apply_extmap_filter_short(struct sk_buff *skb, struct rtpengine_output *o, struct rtp_parsed *rtp) { unsigned char *r = rtp->extension; // reader unsigned char *end = r + rtp->extension_len; unsigned char *w = r; // writer unsigned int count = 0; // non-padding extensions // XXX partly shared code while (r < end) { uint8_t id; uint8_t len; uint8_t id_len = r[0]; if (id_len == '\0') { // padding r++; // don't copy padding to *w continue; } id = id_len >> 4; len = (id_len & 0xf) + 1; if (!apply_extmap_filter_ext(id, len, 1, &r, &w, end, &count, o)) break; } add_extmap_exts_short(&w, &count, o, rtp, skb); apply_extmap_filter_finish(r, w, count, o, skb, rtp); } static void apply_extmap_filter_long(struct sk_buff *skb, struct rtpengine_output *o, struct rtp_parsed *rtp) { unsigned char *r = rtp->extension; // reader unsigned char *end = r + rtp->extension_len; unsigned char *w = r; // writer unsigned int count = 0; // non-padding extensions // XXX partly shared code while (r < end) { uint8_t len; uint8_t id = r[0]; if (id == '\0') { // padding r++; // don't copy padding to *w continue; } len = r[1]; if (!apply_extmap_filter_ext(id, len, 2, &r, &w, end, &count, o)) break; } add_extmap_exts_long(&w, &count, o, rtp, skb); apply_extmap_filter_finish(r, w, count, o, skb, rtp); } static void add_extmap_hdr_long(struct sk_buff *skb, struct rtpengine_output *o, struct rtp_parsed *rtp) { unsigned char *r; unsigned char *w = rtp->payload; // header goes here unsigned int count = 0; unsigned char *hdr = insert_skb_data(&w, skb, rtp, 4); rtp->ext_hdr = (void *) hdr; *hdr++ = 0x10; *hdr++ = 0x00; hdr += 2; // length rtp->extension = (void *) hdr; rtp->extension_len = 0; r = w; add_extmap_exts_long(&w, &count, o, rtp, skb); apply_extmap_filter_finish(r, w, count, o, skb, rtp); } static void add_extmap_hdr_short(struct sk_buff *skb, struct rtpengine_output *o, struct rtp_parsed *rtp) { unsigned char *r; unsigned char *w = rtp->payload; // header goes here unsigned int count = 0; unsigned char *hdr = insert_skb_data(&w, skb, rtp, 4); rtp->ext_hdr = (void *) hdr; *hdr++ = 0xbe; *hdr++ = 0xde; hdr += 2; // length rtp->extension = (void *) hdr; rtp->extension_len = 0; r = w; add_extmap_exts_short(&w, &count, o, rtp, skb); apply_extmap_filter_finish(r, w, count, o, skb, rtp); } static void apply_extmap_filter(struct sk_buff *skb, struct rtpengine_output *o, struct rtp_parsed *rtp) { if (rtp->ext_hdr) { if (ntohs(rtp->ext_hdr->undefined) == 0xbede) apply_extmap_filter_short(skb, o, rtp); else if ((ntohs(rtp->ext_hdr->undefined) & 0xfff0) == 0x0100) apply_extmap_filter_long(skb, o, rtp); // else: leave untouched } else { // add extension header? if (!o->output.extmap_mid) {} // nothing else if (o->output.extmap_mid > 14) add_extmap_hdr_long(skb, o, rtp); else add_extmap_hdr_short(skb, o, rtp); } rtp->header_len = rtp->payload - (unsigned char *) rtp->rtp_header; }