From ea549806c90f02f87a1fc6625e0626602048737c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 17 Jul 2015 20:47:04 +0200 Subject: osmux: introduce osmux_xfrm_input_open_circuit() This new function allows you to create a circuit on an existing input handle. We don't create the circuit anymore from the first packet seen, instead the client application is in full control of opening and closing the circuit. This change includes a new feature to pad a circuit until we see the first packet that contains voice data. This is useful to preallocate bandwidth on satellite links such as Iridium/OpenPort. --- include/osmocom/netif/osmux.h | 4 +- src/osmux.c | 133 +++++++++++++++++++++++++++++++----------- 2 files changed, 103 insertions(+), 34 deletions(-) diff --git a/include/osmocom/netif/osmux.h b/include/osmocom/netif/osmux.h index 14c967f..c1f527a 100644 --- a/include/osmocom/netif/osmux.h +++ b/include/osmocom/netif/osmux.h @@ -5,7 +5,7 @@ /* OSmux header: * - * ft (3 bits): 0=signalling, 1=voice + * ft (3 bits): 0=signalling, 1=voice, 2=dummy * ctr (3 bits): Number of batched AMR payloads (starting 0) * amr_f (1 bit): AMR F field (RFC3267) * amr_q (1 bit): AMR Q field (RFC3267) @@ -17,6 +17,7 @@ #define OSMUX_FT_SIGNAL 0 #define OSMUX_FT_VOICE_AMR 1 +#define OSMUX_FT_DUMMY 2 struct osmux_hdr { #if OSMO_IS_BIG_ENDIAN @@ -84,6 +85,7 @@ int osmux_snprintf(char *buf, size_t size, struct msgb *msg); void osmux_xfrm_input_init(struct osmux_in_handle *h); void osmux_xfrm_input_fini(struct osmux_in_handle *h); +int osmux_xfrm_input_open_circuit(struct osmux_in_handle *h, int ccid, int dummy); void osmux_xfrm_input_close_circuit(struct osmux_in_handle *h, int ccid); int osmux_xfrm_input(struct osmux_in_handle *h, struct msgb *msg, int ccid); diff --git a/src/osmux.c b/src/osmux.c index 4451b5a..74883d9 100644 --- a/src/osmux.c +++ b/src/osmux.c @@ -56,16 +56,29 @@ static uint32_t osmux_get_payload_len(struct osmux_hdr *osmuxh) return osmo_amr_bytes(osmuxh->amr_ft) * (osmuxh->ctr+1); } -struct osmux_hdr *osmux_xfrm_output_pull(struct msgb *msg) +static uint32_t osmux_ft_dummy_size(uint8_t amr_ft, uint32_t batch_factor) { - struct osmux_hdr *osmuxh = NULL; + return sizeof(struct osmux_hdr) + (osmo_amr_bytes(amr_ft) * batch_factor); +} +struct osmux_hdr *osmux_xfrm_output_pull(struct msgb *msg) +{ + struct osmux_hdr *osmuxh; +next: + osmuxh = NULL; if (msg->len > sizeof(struct osmux_hdr)) { size_t len; osmuxh = (struct osmux_hdr *)msg->data; - if (osmuxh->ft != OSMUX_FT_VOICE_AMR) { + switch (osmuxh->ft) { + case OSMUX_FT_VOICE_AMR: + break; + case OSMUX_FT_DUMMY: + msgb_pull(msg, osmux_ft_dummy_size(osmuxh->amr_ft, + osmuxh->ctr + 1)); + goto next; + default: LOGP(DLMIB, LOGL_ERROR, "Discarding unsupported Osmux FT %d\n", osmuxh->ft); return NULL; @@ -185,6 +198,7 @@ struct osmux_batch { unsigned int remaining_bytes; uint8_t seq; uint32_t nmsgs; + int dummy; }; struct osmux_circuit { @@ -192,6 +206,7 @@ struct osmux_circuit { int ccid; struct llist_head msg_list; int nmsgs; + int dummy; }; static int osmux_batch_enqueue(struct msgb *msg, struct osmux_circuit *circuit) @@ -285,8 +300,31 @@ static int osmux_xfrm_encode_amr(struct osmux_batch *batch, return 0; } +static void osmux_encode_dummy(struct osmux_batch *batch, uint32_t batch_factor, + struct osmux_input_state *state) +{ + struct osmux_hdr *osmuxh; + /* TODO: This should be configurable at some point. */ + uint32_t payload_size = osmux_ft_dummy_size(AMR_FT_3, batch_factor) - + sizeof(struct osmux_hdr); + + osmuxh = (struct osmux_hdr *)state->out_msg->tail; + osmuxh->ft = OSMUX_FT_DUMMY; + osmuxh->ctr = batch_factor - 1; + osmuxh->amr_f = 0; + osmuxh->amr_q= 0; + osmuxh->seq = 0; + osmuxh->circuit_id = state->ccid; + osmuxh->amr_cmr = 0; + osmuxh->amr_ft = AMR_FT_3; + msgb_put(state->out_msg, sizeof(struct osmux_hdr)); + + memset(state->out_msg->tail, 0xff, payload_size); + msgb_put(state->out_msg, payload_size); +} + static struct msgb *osmux_build_batch(struct osmux_batch *batch, - uint32_t batch_size) + uint32_t batch_size, uint32_t batch_factor) { struct msgb *batch_msg; struct osmux_circuit *circuit; @@ -305,6 +343,15 @@ static struct msgb *osmux_build_batch(struct osmux_batch *batch, struct msgb *cur, *tmp; int ctr = 0; + if (circuit->dummy) { + struct osmux_input_state state = { + .out_msg = batch_msg, + .ccid = circuit->ccid, + }; + osmux_encode_dummy(batch, batch_factor, &state); + continue; + } + llist_for_each_entry_safe(cur, tmp, &circuit->msg_list, list) { struct osmux_input_state state = { .msg = cur, @@ -348,7 +395,7 @@ void osmux_xfrm_input_deliver(struct osmux_in_handle *h) #ifdef DEBUG_MSG LOGP(DLMIB, LOGL_DEBUG, "invoking delivery function\n"); #endif - batch_msg = osmux_build_batch(batch, h->batch_size); + batch_msg = osmux_build_batch(batch, h->batch_size, h->batch_factor); h->stats.output_osmux_msgs++; h->stats.output_osmux_bytes += batch_msg->len; @@ -356,6 +403,11 @@ void osmux_xfrm_input_deliver(struct osmux_in_handle *h) h->deliver(batch_msg, h->data); osmo_timer_del(&batch->timer); batch->remaining_bytes = h->batch_size; + + if (batch->dummy) { + osmo_timer_schedule(&batch->timer, 0, + h->batch_factor * DELTA_RTP_MSG); + } } static void osmux_batch_timer_expired(void *data) @@ -462,7 +514,8 @@ osmux_batch_find_circuit(struct osmux_batch *batch, int ccid) } static struct osmux_circuit * -osmux_batch_add_circuit(struct osmux_batch *batch, int ccid) +osmux_batch_add_circuit(struct osmux_batch *batch, int ccid, int dummy, + int batch_factor) { struct osmux_circuit *circuit; @@ -482,6 +535,13 @@ osmux_batch_add_circuit(struct osmux_batch *batch, int ccid) INIT_LLIST_HEAD(&circuit->msg_list); llist_add_tail(&circuit->head, &batch->circuit_list); + if (dummy) { + circuit->dummy = dummy; + batch->dummy++; + if (!osmo_timer_pending(&batch->timer)) + osmo_timer_schedule(&batch->timer, 0, + batch_factor * DELTA_RTP_MSG); + } return circuit; } @@ -493,6 +553,8 @@ static void osmux_batch_del_circuit(struct osmux_batch *batch, int ccid) if (circuit == NULL) return; + if (circuit->dummy) + batch->dummy--; llist_del(&circuit->head); talloc_free(circuit); } @@ -503,51 +565,48 @@ osmux_batch_add(struct osmux_batch *batch, int batch_factor, struct msgb *msg, { int bytes = 0, amr_payload_len; struct osmux_circuit *circuit; + struct msgb *cur; circuit = osmux_batch_find_circuit(batch, ccid); + if (!circuit) + return -1; + /* We've seen the first RTP message, disable dummy padding */ + if (circuit->dummy) { + circuit->dummy = 0; + batch->dummy--; + } amr_payload_len = osmux_rtp_amr_payload_len(msg, rtph); if (amr_payload_len < 0) return -1; /* First check if there is room for this message in the batch */ bytes += amr_payload_len; - if (!circuit || circuit->nmsgs == 0) + if (circuit->nmsgs == 0) bytes += sizeof(struct osmux_hdr); /* No room, sorry. You'll have to retry */ if (bytes > batch->remaining_bytes) return 1; - if (circuit) { - struct msgb *cur; - - /* Extra validation: check if this message already exists, - * should not happen but make sure we don't propagate - * duplicated messages. - */ - llist_for_each_entry(cur, &circuit->msg_list, list) { - struct rtp_hdr *rtph2 = osmo_rtp_get_hdr(cur); - if (rtph2 == NULL) - return -1; - - /* Already exists message with this sequence, skip */ - if (rtph2->sequence == rtph->sequence) { - LOGP(DLMIB, LOGL_ERROR, "already exists " - "message with seq=%u, skip it\n", - rtph->sequence); - return -1; - } - } - /* Handle RTP packet loss scenario */ - osmux_replay_lost_packets(circuit, rtph); + /* Extra validation: check if this message already exists, should not + * happen but make sure we don't propagate duplicated messages. + */ + llist_for_each_entry(cur, &circuit->msg_list, list) { + struct rtp_hdr *rtph2 = osmo_rtp_get_hdr(cur); + if (rtph2 == NULL) + return -1; - } else { - /* This is the first message with that ssrc we've seen */ - circuit = osmux_batch_add_circuit(batch, ccid); - if (!circuit) + /* Already exists message with this sequence, skip */ + if (rtph2->sequence == rtph->sequence) { + LOGP(DLMIB, LOGL_ERROR, "already exists " + "message with seq=%u, skip it\n", + rtph->sequence); return -1; + } } + /* Handle RTP packet loss scenario */ + osmux_replay_lost_packets(circuit, rtph); /* This batch is full, force batch delivery */ if (osmux_batch_enqueue(msg, circuit) < 0) @@ -655,6 +714,14 @@ void osmux_xfrm_input_init(struct osmux_in_handle *h) LOGP(DLMIB, LOGL_DEBUG, "initialized osmux input converter\n"); } +int osmux_xfrm_input_open_circuit(struct osmux_in_handle *h, int ccid, + int dummy) +{ + struct osmux_batch *batch = (struct osmux_batch *)h->internal_data; + + return osmux_batch_add_circuit(batch, ccid, dummy, h->batch_factor) ? 0 : -1; +} + void osmux_xfrm_input_close_circuit(struct osmux_in_handle *h, int ccid) { struct osmux_batch *batch = (struct osmux_batch *)h->internal_data; -- cgit v1.2.3