/* * This file is part of GAPK (GSM Audio Pocket Knife). * * (C) 2017 by Vadim Yanitskiy * * GAPK is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GAPK is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GAPK. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include /** * This test is intended to check the RTP source / sink operability. * To do this, two processing queues are being allocated: * * "generator": source/random -> sink/rtp * "checker": source/rtp -> sink/checker * * The first one generates some amount of random bytes (payload), * and stores them inside a buffer that is shared between both * queues. * * After generation, a payload is being sent from the first * queue via an RTP sink, and then being received by the second * via an RTP source. * * As both queues do use a shared buffer, the last item of the * second queue (named 'sink/checker') is able to compare a * received payload with expected. */ static void talloc_ctx_walk_cb(const void *chunk, int depth, int max_depth, int is_ref, void *data) { const char *chunk_name = talloc_get_name(chunk); int spaces_cnt; /* Hierarchical spacing */ for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++) printf(" "); /* Chunk info */ printf("chunk %s: depth=%d\n", chunk_name, depth); } #define RTP_TEST_BUF_LEN 128 struct rtp_test_state { unsigned int payload_len; unsigned int rtp_port; int rtp_src_fd; int rtp_dst_fd; uint8_t data[RTP_TEST_BUF_LEN]; uint8_t *ptr; }; static int src_rand_proc(void *data, uint8_t *out, const uint8_t *in, unsigned int in_len) { struct rtp_test_state *state = (struct rtp_test_state *) data; unsigned int i; /* Generate a random payload */ for (i = 0; i < state->payload_len; i++) { uint8_t byte = rand() % 0xff; *(state->ptr + i) = byte; out[i] = byte; } return state->payload_len; } static void src_rand_exit(void *data) { struct rtp_test_state *state = (struct rtp_test_state *) data; if (state->rtp_src_fd >= 0) { close(state->rtp_src_fd); state->rtp_src_fd = -1; } } static int sink_chk_proc(void *data, uint8_t *out, const uint8_t *in, unsigned int in_len) { struct rtp_test_state *state = (struct rtp_test_state *) data; unsigned int i; /* Make sure we have all bytes transferred */ if (in_len != state->payload_len) { printf("Data length mismatch!\n"); return -EINVAL; } for (i = 0; i < in_len; i++) { if (in[i] != *(state->ptr + i)) { printf("Data mismatch!\n"); return -EINVAL; } } return in_len; } static void sink_chk_exit(void *data) { struct rtp_test_state *state = (struct rtp_test_state *) data; if (state->rtp_dst_fd >= 0) { close(state->rtp_dst_fd); state->rtp_dst_fd = -1; } } /* Allocates: source/random -> sink/rtp */ static int init_gen_queue(struct osmo_gapk_pq *pq, struct rtp_test_state *state, unsigned int payload_len) { int rc; /* Allocate memory for the 'source/random' */ struct osmo_gapk_pq_item *src_rand = osmo_gapk_pq_add_item(pq); if (!src_rand) return -ENOMEM; /* Fill in meta information */ src_rand->type = OSMO_GAPK_ITEM_TYPE_SOURCE; src_rand->cat_name = OSMO_GAPK_CAT_NAME_SOURCE; src_rand->sub_name = "random"; /* Set I/O buffer lengths */ state->payload_len = payload_len; src_rand->len_out = payload_len; /* Set proc / exit callbacks and state */ src_rand->proc = &src_rand_proc; src_rand->exit = &src_rand_exit; src_rand->state = state; /* Init connection socket */ state->rtp_dst_fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", state->rtp_port, OSMO_SOCK_F_CONNECT); if (state->rtp_dst_fd < 0) { printf("Could not init connection socket\n"); return -EINVAL; } /* Init an RTP sink */ rc = osmo_gapk_pq_queue_rtp_output(pq, state->rtp_dst_fd, payload_len, 0x00); if (rc) { printf("Could not init an RTP sink\n"); return rc; } /* Check and prepare */ rc = osmo_gapk_pq_check(pq, 1); if (rc) { printf("Queue check failed\n"); return rc; } rc = osmo_gapk_pq_prepare(pq); if (rc) { printf("Queue preparation failed\n"); return rc; } return 0; } /* Allocates: source/rtp -> sink/checker */ static int init_chk_queue(struct osmo_gapk_pq *pq, struct rtp_test_state *state, unsigned int payload_len) { int rc; /* Init listening socket */ state->rtp_src_fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, OSMO_SOCK_F_BIND); if (state->rtp_src_fd < 0) { printf("Could not init listening socket\n"); return -EINVAL; } /* Init an RTP source on any available port */ rc = osmo_gapk_pq_queue_rtp_input(pq, state->rtp_src_fd, payload_len, 0x00); if (rc) { printf("Could not init an RTP sink\n"); return rc; } /* Determine on which port are we listening */ struct sockaddr_in adr_inet; socklen_t len_inet; len_inet = sizeof(adr_inet); rc = getsockname(state->rtp_src_fd, (struct sockaddr *) &adr_inet, &len_inet); if (rc) return -EINVAL; /* Save assigned port to shared state */ state->rtp_port = (unsigned int) ntohs(adr_inet.sin_port); /* Allocate memory for the 'sink/checker' */ struct osmo_gapk_pq_item *sink_chk = osmo_gapk_pq_add_item(pq); if (!sink_chk) return -ENOMEM; /* Fill in meta information */ sink_chk->type = OSMO_GAPK_ITEM_TYPE_SINK; sink_chk->cat_name = OSMO_GAPK_CAT_NAME_SINK; sink_chk->sub_name = "checker"; /* Set I/O buffer lengths */ sink_chk->len_in = payload_len; /* Set proc / exit callbacks and state */ sink_chk->proc = &sink_chk_proc; sink_chk->exit = &sink_chk_exit; sink_chk->state = state; /* Check and prepare */ rc = osmo_gapk_pq_check(pq, 1); if (rc) { printf("Queue check failed\n"); return rc; } rc = osmo_gapk_pq_prepare(pq); if (rc) { printf("Queue preparation failed\n"); return rc; } return 0; } static int rtp_test(struct rtp_test_state *state, unsigned int payload_len) { struct osmo_gapk_pq *q_gen, *q_chk; unsigned int i, chunks; int rc; /* Allocate two queues */ q_gen = osmo_gapk_pq_create("generator"); q_chk = osmo_gapk_pq_create("checker"); /* Make sure both queues are allocated */ if (!q_gen || !q_chk) { rc = -ENOMEM; goto exit; } /* Init both queues: generator and checker */ rc = init_chk_queue(q_chk, state, payload_len); if (rc) goto exit; rc = init_gen_queue(q_gen, state, payload_len); if (rc) goto exit; /* Calculate how much chunks do we have */ chunks = RTP_TEST_BUF_LEN / payload_len; /* Execute both queues */ for (i = 0; i < chunks; i++) { /* Move data pointer */ state->ptr = state->data + i * payload_len; /* Generate and send a payload */ rc = osmo_gapk_pq_execute(q_gen); if (rc) { printf("Queue '%s' execution aborted on chunk %u/%u\n", q_gen->name, i + 1, chunks); goto exit; } /* TODO: prevent test hang if nothing was being sent */ /* Receive and check a payload */ rc = osmo_gapk_pq_execute(q_chk); if (rc) { printf("Queue '%s' execution aborted on chunk %u/%u\n", q_gen->name, i + 1, chunks); goto exit; } } printf("Payload len=%u check ok\n", payload_len); exit: /* Deallocate both queues and data */ osmo_gapk_pq_destroy(q_gen); osmo_gapk_pq_destroy(q_chk); return rc; } int main(int argc, char **argv) { struct rtp_test_state state; unsigned int len; /* Enable tracking the use of NULL memory contexts */ talloc_enable_null_tracking(); /* Init pseudo-random generator */ srand(time(NULL)); /* Perform testing with different payload size values */ for (len = 1; len <= RTP_TEST_BUF_LEN; len *= 2) assert(rtp_test(&state, len) == 0); printf("\n"); /* Memory leak detection test */ talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL); /* Make both Valgrind and LeakSanitizer happy */ talloc_disable_null_tracking(); return 0; }