aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <Andreas.Eversberg@versatel.de>2009-11-27 09:16:56 +0100
committerHarald Welte <laforge@gnumonks.org>2009-11-27 09:16:56 +0100
commit260b7ddb239d0d497ee7bb7c8c93d52a1c0fe88c (patch)
tree81f47c4204d266da9056c11f57d946b299e0789e
parent53f4b10b210a962faeb37c3e0ed09ae66a83630e (diff)
[LCR] Integration of RTP support into the LCR interface
here is the patch to support audio exchange between application interface and nanoBTS. the patch changes the audio format from TRAU frame format to RTP payload format on the application interface. also the BS11 TRAU frame is converted to/from RTP payload format (inside trau_mux.c), so an application does not have to care about what BTS is used. here is the format as defined by RFC: (just fyi) first byte has the magic value of 0xd0 plus four upper bits of the LARc[0] element as lower bits. second byte has the lower two bits of the LARc[0] element as upper bits and the upper bits of LARc[1] element as lower bits. third byte has the lower four bits of LARc[1] element as upper bits and so on... this format can directly be transcoded with the open source lossy gsm codec. the rtp_proxy.c now parses RTP frames and generates them. for easier code, i just mirrored the received RTCP frames back to the nanoBTS, mangled of yourse. (oops, maybe i need to change some synchronization source identifiers.) i think there is no problem with that right now. because RFC requires special synchronization source (ssrc) generation, i added md5 code and the (funny) random number generator. (see rtp_proxy.c random32()) as soon as the patch is applied, i will also check in linux-call-router modification for the new audio frame format. note that this code is just quickly tested, it works with both BS11 and nanoBTS and should not crash on corrupt or extended RTP data.
-rw-r--r--openbsc/include/openbsc/md5.h59
-rw-r--r--openbsc/include/openbsc/mncc.h4
-rw-r--r--openbsc/include/openbsc/rtp_proxy.h16
-rw-r--r--openbsc/include/openbsc/transaction.h3
-rw-r--r--openbsc/include/openbsc/trau_mux.h2
-rw-r--r--openbsc/src/Makefile.am2
-rw-r--r--openbsc/src/gsm_04_08.c113
-rw-r--r--openbsc/src/md5c.c323
-rw-r--r--openbsc/src/rtp_proxy.c278
-rw-r--r--openbsc/src/trau_mux.c90
10 files changed, 838 insertions, 52 deletions
diff --git a/openbsc/include/openbsc/md5.h b/openbsc/include/openbsc/md5.h
new file mode 100644
index 000000000..938c257a9
--- /dev/null
+++ b/openbsc/include/openbsc/md5.h
@@ -0,0 +1,59 @@
+/*
+ * MD5.H - header file for MD5C.C
+ */
+
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights
+ * reserved.
+ *
+ * License to copy and use this software is granted provided that it is
+ * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm"
+ * in all material mentioning or referencing this software or this function.
+ *
+ * License is also granted to make and use derivative works provided that such
+ * works are identified as "derived from the RSA Data Security, Inc. MD5
+ * Message-Digest Algorithm" in all material mentioning or referencing the
+ * derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either the
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#ifndef _MD5_H
+#define _MD5_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+/* MD5 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb
+ * first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init (MD5_CTX *);
+void MD5Update (MD5_CTX *, unsigned char *, unsigned int);
+void MD5Final (unsigned char[16], MD5_CTX *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* md5.h */
diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h
index 68d76abf8..4b749e8ac 100644
--- a/openbsc/include/openbsc/mncc.h
+++ b/openbsc/include/openbsc/mncc.h
@@ -87,7 +87,7 @@ struct gsm_call {
#define MNCC_FRAME_DROP 0x0202
#define MNCC_LCHAN_MODIFY 0x0203
-#define GSM_TRAU_FRAME 0x0300
+#define GSM_TCHF_FRAME 0x0300
#define GSM_MAX_FACILITY 128
#define GSM_MAX_SSVERSION 128
@@ -199,7 +199,7 @@ struct gsm_mncc {
unsigned char lchan_mode;
};
-struct gsm_trau_frame {
+struct gsm_data_frame {
u_int32_t msg_type;
u_int32_t callref;
unsigned char data[0];
diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h
index e9fc157cf..a3e45964c 100644
--- a/openbsc/include/openbsc/rtp_proxy.h
+++ b/openbsc/include/openbsc/rtp_proxy.h
@@ -34,6 +34,11 @@ enum rtp_rx_action {
RTP_RECV_UPSTREAM,
};
+enum rtp_tx_action {
+ RTP_SEND_NONE,
+ RTP_SEND_DOWNSTREAM,
+};
+
struct rtp_sub_socket {
struct sockaddr_in sin_local;
struct sockaddr_in sin_remote;
@@ -56,15 +61,24 @@ struct rtp_socket {
struct rtp_socket *other_sock;
} proxy;
struct {
- void (*recv_cb)(struct msgb *msg);
+ struct gsm_network *net;
+ u_int32_t callref;
} receive;
};
+ enum rtp_tx_action tx_action;
+ struct {
+ u_int16_t sequence;
+ u_int32_t timestamp;
+ u_int32_t ssrc;
+ } transmit;
};
struct rtp_socket *rtp_socket_create(void);
int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip);
int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port);
int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other);
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, u_int32_t callref);
int rtp_socket_free(struct rtp_socket *rs);
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame);
#endif /* _RTP_PROXY_H */
diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h
index 961a64923..6ffe25476 100644
--- a/openbsc/include/openbsc/transaction.h
+++ b/openbsc/include/openbsc/transaction.h
@@ -26,6 +26,9 @@ struct gsm_trans {
/* reference from MNCC or other application */
u_int32_t callref;
+ /* if traffic channel receive was requested */
+ int tch_recv;
+
union {
struct {
diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h
index 90535add4..8deb708de 100644
--- a/openbsc/include/openbsc/trau_mux.h
+++ b/openbsc/include/openbsc/trau_mux.h
@@ -46,4 +46,4 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref);
/* send trau from application */
-int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf);
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame);
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 919e07574..726c71278 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -13,7 +13,7 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
talloc_ctx.c
libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
- mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \
+ mncc.c rtp_proxy.c md5c.c gsm_04_08.c gsm_04_11.c transaction.c \
token_auth.c rrlp.c
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 9467d79a6..ba4d07e2b 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1845,6 +1845,8 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
return 0;
}
+static int tch_recv(struct gsm_network *net, u_int32_t callref, int enable);
+
/* some other part of the code sends us a signal */
static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
@@ -1852,6 +1854,8 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
struct gsm_lchan *lchan = signal_data;
struct gsm_bts_trx_ts *ts;
int rc;
+ struct gsm_network *net;
+ struct gsm_trans *trans;
if (subsys != SS_ABISIP)
return 0;
@@ -1880,6 +1884,14 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
ts->abis_ip.bound_port);
if (rc < 0)
goto out_err;
+ /* receive if whish is pending */
+ net = ts->trx->bts->network;
+ llist_for_each_entry(trans, &net->trans_list, entry) {
+ if (trans->lchan == lchan && trans->tch_recv) {
+ DEBUGP(DCC, "is pending tch_recv whish\n");
+ tch_recv(net, trans->callref, 1);
+ }
+ }
break;
case S_ABISIP_DISC_IND:
/* the BTS tells us a RTP stream has been disconnected */
@@ -1928,7 +1940,8 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n");
return -EINVAL;
}
-
+
+ // todo: map between different bts types
switch (bts->type) {
case GSM_BTS_TYPE_NANOBTS:
if (!ipacc_rtp_direct) {
@@ -1937,6 +1950,7 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
if (rc < 0)
return rc;
rc = ipacc_connect_proxy_bind(remote_lchan);
+#warning do we need a check of rc here?
/* connect them with each other */
rtp_socket_proxy(lchan->ts->abis_ip.rtp_socket,
@@ -1962,8 +1976,7 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
break;
default:
DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
- rc = -EINVAL;
- break;
+ return -EINVAL;
}
return 0;
@@ -1986,44 +1999,59 @@ static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
}
/* enable receive of channels to upqueue */
-static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+static int tch_recv(struct gsm_network *net, u_int32_t callref, int enable)
{
struct gsm_trans *trans;
+ struct gsm_lchan *lchan;
+ struct gsm_bts *bts;
+ int rc;
/* Find callref */
- trans = trans_find_by_callref(net, data->callref);
+ trans = trans_find_by_callref(net, callref);
if (!trans)
return -EIO;
if (!trans->lchan)
return 0;
+ lchan = trans->lchan;
+ bts = lchan->ts->trx->bts;
- // todo IPACCESS
- if (enable)
- return trau_recv_lchan(trans->lchan, data->callref);
- return trau_mux_unmap(NULL, data->callref);
-}
-
-/* send a frame to channel */
-static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
-{
- struct gsm_trans *trans;
-
- /* Find callref */
- trans = trans_find_by_callref(net, frame->callref);
- if (!trans)
- return -EIO;
- if (!trans->lchan)
- return 0;
- if (trans->lchan->type != GSM_LCHAN_TCH_F &&
- trans->lchan->type != GSM_LCHAN_TCH_H)
- return 0;
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (ipacc_rtp_direct) {
+ DEBUGP(DCC, "Error: RTP proxy is disabled\n");
+ return -EINVAL;
+ }
+ /* in case, we don't have a socket yet */
+ if (!lchan->ts->abis_ip.rtp_socket) {
+ trans->tch_recv = enable;
+ DEBUGP(DCC, "queue tch_recv whish (%d)\n", enable);
+ return 0;
+ }
+ if (enable) {
+ /* connect the TCH's to our RTP proxy */
+ rc = ipacc_connect_proxy_bind(lchan);
+ if (rc < 0)
+ return rc;
+ /* assign socket to application interface */
+ rtp_socket_upstream(lchan->ts->abis_ip.rtp_socket,
+ net, callref);
+ } else
+ rtp_socket_upstream(lchan->ts->abis_ip.rtp_socket,
+ net, 0);
+ break;
+ case GSM_BTS_TYPE_BS11:
+ if (enable)
+ return trau_recv_lchan(lchan, callref);
+ return trau_mux_unmap(NULL, callref);
+ break;
+ default:
+ DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+ return -EINVAL;
+ }
- // todo IPACCESS
- return trau_send_lchan(trans->lchan,
- (struct decoded_trau_frame *)frame->data);
+ return 0;
}
-
static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
{
DEBUGP(DCC, "-> STATUS ENQ\n");
@@ -3262,11 +3290,30 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
case MNCC_BRIDGE:
return tch_bridge(net, arg);
case MNCC_FRAME_DROP:
- return tch_recv(net, arg, 0);
+ return tch_recv(net, data->callref, 0);
case MNCC_FRAME_RECV:
- return tch_recv(net, arg, 1);
- case GSM_TRAU_FRAME:
- return tch_frame(net, arg);
+ return tch_recv(net, data->callref, 1);
+ case GSM_TCHF_FRAME:
+ /* Find callref */
+ trans = trans_find_by_callref(net, data->callref);
+ if (!trans)
+ return -EIO;
+ if (!trans->lchan)
+ return 0;
+ if (trans->lchan->type != GSM_LCHAN_TCH_F)
+ return 0;
+ bts = trans->lchan->ts->trx->bts;
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (!trans->lchan->ts->abis_ip.rtp_socket)
+ return 0;
+ return rtp_send_frame(trans->lchan->ts->abis_ip.rtp_socket, arg);
+ case GSM_BTS_TYPE_BS11:
+ return trau_send_frame(trans->lchan, arg);
+ default:
+ DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+ }
+ return -EINVAL;
}
memset(&rel, 0, sizeof(struct gsm_mncc));
diff --git a/openbsc/src/md5c.c b/openbsc/src/md5c.c
new file mode 100644
index 000000000..4b1d15e9a
--- /dev/null
+++ b/openbsc/src/md5c.c
@@ -0,0 +1,323 @@
+/*
+ * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights
+ * reserved.
+ *
+ * License to copy and use this software is granted provided that it is
+ * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm"
+ * in all material mentioning or referencing this software or this function.
+ *
+ * License is also granted to make and use derivative works provided that such
+ * works are identified as "derived from the RSA Data Security, Inc. MD5
+ * Message-Digest Algorithm" in all material mentioning or referencing the
+ * derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either the
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include "openbsc/md5.h"
+
+#include <string.h>
+
+/*
+ * Constants for MD5Transform routine.
+ */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform (UINT4[4], unsigned char[64]);
+static void Encode (unsigned char *, UINT4 *, unsigned int);
+static void Decode (UINT4 *, unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/*
+ * ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is
+ * separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/*
+ * MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void
+MD5Init( MD5_CTX *context)/* context */
+{
+ context->count[0] = context->count[1] = 0;
+ /*
+ * Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/*
+ * MD5 block update operation. Continues an MD5 message-digest operation,
+ * processing another message block, and updating the context.
+ */
+void
+MD5Update(
+ MD5_CTX *context,/* context */
+ unsigned char *input, /* input block */
+ unsigned int inputLen) /* length of input block */
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4) inputLen << 3))
+ < ((UINT4) inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4) inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /*
+ * Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ memcpy
+ ((POINTER) & context->buffer[index], (POINTER) input, partLen);
+ MD5Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform(context->state, &input[i]);
+
+ index = 0;
+ } else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy
+ ((POINTER) & context->buffer[index], (POINTER) & input[i],
+ inputLen - i);
+}
+
+/*
+ * MD5 finalization. Ends an MD5 message-digest operation, writing the the
+ * message digest and zeroizing the context.
+ */
+void
+MD5Final(
+ unsigned char digest[16], /* message digest */
+ MD5_CTX *context) /* context */
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode(bits, context->count, 8);
+
+ /*
+ * Pad out to 56 mod 64.
+ */
+ index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+
+ /* Store state in digest */
+ Encode(digest, context->state, 16);
+
+ /*
+ * Zeroize sensitive information.
+ */
+ memset((POINTER) context, 0, sizeof(*context));
+}
+
+/*
+ * MD5 basic transformation. Transforms state based on block.
+ */
+static void
+MD5Transform(
+ UINT4 state[4],
+ unsigned char block[64])
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3],
+ x[16];
+
+ Decode(x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /*
+ * Zeroize sensitive information.
+ */
+ memset((POINTER) x, 0, sizeof(x));
+}
+
+/*
+ * Encodes input (UINT4) into output (unsigned char). Assumes len is a
+ * multiple of 4.
+ */
+static void
+Encode( unsigned char *output,
+ UINT4 *input,
+ unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char) (input[i] & 0xff);
+ output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
+ output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
+ output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
+ }
+}
+
+/*
+ * Decodes input (unsigned char) into output (UINT4). Assumes len is a
+ * multiple of 4.
+ */
+static void
+Decode( UINT4 *output,
+ unsigned char *input,
+ unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) |
+ (((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24);
+}
+
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
index 59c0735a5..b92526e24 100644
--- a/openbsc/src/rtp_proxy.c
+++ b/openbsc/src/rtp_proxy.c
@@ -24,6 +24,11 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <sys/time.h> /* gettimeofday() */
+#include <unistd.h> /* get..() */
+#include <time.h> /* clock() */
+#include <sys/utsname.h> /* uname() */
+#include "openbsc/md5.h" /* from RFC 1321 */
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
@@ -57,6 +62,220 @@ struct rtcp_hdr {
#define RTCP_IE_CNAME 1
+/* according to RFC 3550 */
+struct rtp_hdr {
+ u_int8_t csrc_count:4,
+ extension:1,
+ padding:1,
+ version:2;
+ u_int8_t payload_type:7,
+ marker:1;
+ u_int16_t sequence;
+ u_int32_t timestamp;
+ u_int32_t ssrc;
+} __attribute__((packed));
+
+struct rtp_x_hdr {
+ u_int16_t by_profile;
+ u_int16_t length;
+} __attribute__((packed));
+
+#define RTP_VERSION 2
+
+#define RTP_PT_GSM_FULL 3
+
+/*
+ * Generate a random 32-bit quantity. (Appendix A. 6)
+ */
+
+#define MD_CTX MD5_CTX
+#define MDInit MD5Init
+#define MDUpdate MD5Update
+#define MDFinal MD5Final
+
+static u_int32_t md_32(unsigned char *string, int length)
+{
+ MD_CTX context;
+ union {
+ char c[16];
+ u_int32_t x[4];
+ } digest;
+ u_int32_t r;
+ int i;
+
+ MDInit (&context);
+ MDUpdate (&context, string, length);
+ MDFinal ((unsigned char *)&digest, &context);
+ r = 0;
+ for (i = 0; i < 3; i++) {
+ r ^= digest.x[i];
+ }
+ return r;
+}
+
+/*
+ * Return random unsigned 32-bit quantity. Use 'type' argument if
+ * you need to generate several different values in close succession.
+ */
+static u_int32_t random32(int type)
+{
+ struct {
+ int type;
+ struct timeval tv;
+ clock_t cpu;
+ pid_t pid;
+ u_int32_t hid;
+ uid_t uid;
+ gid_t gid;
+ struct utsname name;
+ } s;
+
+ gettimeofday(&s.tv, 0);
+ uname(&s.name);
+ s.type = type;
+ s.cpu = clock();
+ s.pid = getpid();
+ s.hid = gethostid();
+ s.uid = getuid();
+ s.gid = getgid();
+ /* also: system uptime */
+
+ return md_32((unsigned char *)&s, sizeof(s));
+}
+
+/* decode an rtp frame and create a new buffer with payload */
+static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
+{
+ struct msgb *new_msg;
+ struct gsm_data_frame *frame;
+ struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
+ struct rtp_x_hdr *rtpxh;
+ u_int8_t *payload;
+ int payload_len;
+ int msg_type;
+ int x_len;
+
+ if (msg->len < 12) {
+ DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n", msg->len);
+ return -EINVAL;
+ }
+ if (rtph->version != RTP_VERSION) {
+ DEBUGPC(DMUX, "received RTP version %d not supported.\n", rtph->version);
+ return -EINVAL;
+ }
+ payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
+ payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short (len = %d, csrc count = %d)\n",
+ msg->len, rtph->csrc_count);
+ return -EINVAL;
+ }
+ if (rtph->extension) {
+ if (payload_len < sizeof(struct rtp_x_hdr)) {
+ DEBUGPC(DMUX, "received RTP frame too short for extension header\n");
+ return -EINVAL;
+ }
+ rtpxh = (struct rtp_x_hdr *)payload;
+ x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
+ payload += x_len;
+ payload_len -= x_len;
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short, extension header exceeds frame length\n");
+ return -EINVAL;
+ }
+ }
+ if (rtph->padding) {
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short for padding length\n");
+ return -EINVAL;
+ }
+ payload_len -= payload[payload_len - 1];
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame with padding greater than payload\n");
+ return -EINVAL;
+ }
+ }
+
+ switch (rtph->payload_type) {
+ case RTP_PT_GSM_FULL:
+ msg_type = GSM_TCHF_FRAME;
+ if (payload_len != 33) {
+ DEBUGPC(DMUX, "received RTP full rate frame with payload length != 32 (len = %d)\n",
+ payload_len);
+ return -EINVAL;
+ }
+ break;
+ default:
+ DEBUGPC(DMUX, "received RTP frame with unknown payload type %d\n",
+ rtph->payload_type);
+ return -EINVAL;
+ }
+
+ new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len, "GSM-DATA");
+ if (!new_msg)
+ return -ENOMEM;
+ frame = (struct gsm_data_frame *)(new_msg->data);
+ frame->msg_type = msg_type;
+ frame->callref = callref;
+ memcpy(frame->data, payload, payload_len);
+ msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
+
+ *data = new_msg;
+ return 0;
+}
+
+/* encode and send a rtp frame */
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
+{
+ struct rtp_sub_socket *rss = &rs->rtp;
+ struct msgb *msg;
+ struct rtp_hdr *rtph;
+ int payload_type;
+ int payload_len;
+ int duration; /* in samples */
+
+ if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
+ /* initialize sequences */
+ rs->tx_action = RTP_SEND_DOWNSTREAM;
+ rs->transmit.ssrc = random32(frame->callref);
+ rs->transmit.sequence = random();
+ rs->transmit.timestamp = random();
+ }
+
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ payload_type = RTP_PT_GSM_FULL;
+ payload_len = 33;
+ duration = 160;
+ break;
+ default:
+ DEBUGPC(DMUX, "unsupported message type %d\n",
+ frame->msg_type);
+ return -EINVAL;
+ }
+
+ msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
+ if (!msg)
+ return -ENOMEM;
+ rtph = (struct rtp_hdr *)msg->data;
+ rtph->version = RTP_VERSION;
+ rtph->padding = 0;
+ rtph->extension = 0;
+ rtph->csrc_count = 0;
+ rtph->marker = 0;
+ rtph->payload_type = payload_type;
+ rtph->sequence = htons(rs->transmit.sequence++);
+ rtph->timestamp = htonl(rs->transmit.timestamp);
+ rs->transmit.timestamp += duration;
+ rtph->ssrc = htonl(rs->transmit.ssrc);
+ memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
+ msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
+ msgb_enqueue(&rss->tx_queue, msg);
+ rss->bfd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
/* iterate over all chunks in one RTCP message, look for CNAME IEs and
* replace all of those with 'new_cname' */
static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
@@ -123,10 +342,15 @@ static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
if (!mangle_rtcp_cname)
return 0;
+ printf("RTCP\n");
/* iterate over list of RTCP messages */
rtph = (struct rtcp_hdr *)msg->data;
- while ((void *)rtph + sizeof(*rtph) < (void *)msg->data + msg->len) {
+ while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
old_len = (ntohs(rtph->length) + 1) * 4;
+ if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
+ DEBUGPC(DMUX, "received RTCP packet too short for lengt element\n");
+ return -EINVAL;
+ }
if (rtph->type == RTCP_TYPE_SDES) {
char new_cname[255];
strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
@@ -148,6 +372,7 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
{
int rc;
struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
+ struct msgb *new_msg;
struct rtp_sub_socket *other_rss;
if (!msg)
@@ -184,13 +409,40 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
break;
case RTP_RECV_UPSTREAM:
- case RTP_NONE:
- /* FIXME: other cases */
- DEBUGP(DMUX, "unhandled action: %d\n", rs->rx_action);
+ if (!rs->receive.callref || !rs->receive.net) {
+ rc = -EIO;
+ goto out_free;
+ }
+ if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+ if (!mangle_rtcp_cname) {
+ msgb_free(msg);
+ break;
+ }
+ /* modify RTCP SDES CNAME */
+ rc = rtcp_mangle(msg, rs);
+ if (rc < 0)
+ goto out_free;
+ msgb_enqueue(&rss->tx_queue, msg);
+ rss->bfd.when |= BSC_FD_WRITE;
+ break;
+ }
+ if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
+ rc = -EINVAL;
+ goto out_free;
+ }
+ rc = rtp_decode(msg, rs->receive.callref, &new_msg);
+ if (rc < 0)
+ goto out_free;
+ msgb_free(msg);
+ msgb_enqueue(&rs->receive.net->upqueue, new_msg);
+ break;
+
+ case RTP_NONE: /* if socket exists, but disabled by app */
+ msgb_free(msg);
break;
}
- return rc;
+ return 0;
out_free:
msgb_free(msg);
@@ -420,6 +672,22 @@ int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
return 0;
}
+/* bind RTP/RTCP socket to application */
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, u_int32_t callref)
+{
+ DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%lu)\n",
+ this, callref);
+
+ if (callref) {
+ this->rx_action = RTP_RECV_UPSTREAM;
+ this->receive.net = net;
+ this->receive.callref = callref;
+ } else
+ this->rx_action = RTP_NONE;
+
+ return 0;
+}
+
static void free_tx_queue(struct rtp_sub_socket *rss)
{
struct msgb *msg;
diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
index 6a19f0c99..b060657c1 100644
--- a/openbsc/src/trau_mux.c
+++ b/openbsc/src/trau_mux.c
@@ -32,6 +32,19 @@
#include <openbsc/debug.h>
#include <openbsc/talloc.h>
+u_int8_t gsm_fr_map[] = {
+ 6, 6, 5, 5, 4, 4, 3, 3,
+ 7, 2, 2, 6, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 7, 2, 2, 6, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 7, 2, 2, 6, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 7, 2, 2, 6, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3
+};
+
struct map_entry {
struct llist_head list;
struct gsm_e1_subslot src, dst;
@@ -151,8 +164,6 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
struct subch_mux *mx;
struct upqueue_entry *ue;
- struct msgb *msg;
- struct gsm_trau_frame *frame;
int rc;
/* decode TRAU, change it to downlink, re-encode */
@@ -161,19 +172,44 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
return rc;
if (!dst_e1_ss) {
+ struct msgb *msg;
+ struct gsm_data_frame *frame;
+ unsigned char *data;
+ int i, j, k, l, o;
/* frame shall be sent to upqueue */
if (!(ue = lookup_trau_upqueue(src_e1_ss)))
return -EINVAL;
if (!ue->callref)
return -EINVAL;
- msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf),
- "TRAU");
+ if (tf.c_bits[0]!=0 || tf.c_bits[1]!=0 || tf.c_bits[2]!=0 || tf.c_bits[3]!=1 || tf.c_bits[4]!=0)
+ DEBUGPC(DMUX, "illegal trau (C1-C5) %d %d %d %d %d\n", tf.c_bits[0], tf.c_bits[1],
+ tf.c_bits[2], tf.c_bits[3], tf.c_bits[4]);
+ msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
+ "GSM-DATA");
if (!msg)
return -ENOMEM;
- frame = (struct gsm_trau_frame *)msg->data;
- frame->msg_type = GSM_TRAU_FRAME;
+
+ frame = (struct gsm_data_frame *)msg->data;
+ memset(frame, 0, sizeof(struct gsm_data_frame));
+ data = frame->data;
+ data[0] = 0xd << 4;
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts output bits */
+ k = gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset input bits */
+ while(i < 260) {
+ data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
+ if (--k < 0) {
+ o += gsm_fr_map[l];
+ k = gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+ frame->msg_type = GSM_TCHF_FRAME;
frame->callref = ue->callref;
- memcpy(frame->data, &tf, sizeof(tf));
msgb_enqueue(&ue->net->upqueue, msg);
return 0;
@@ -219,17 +255,53 @@ int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
return 0;
}
-int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf)
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
{
u_int8_t trau_bits_out[TRAU_FRAME_BITS];
struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
struct subch_mux *mx;
+ int i, j, k, l, o;
+ unsigned char *data = frame->data;
+ struct decoded_trau_frame tf;
mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
if (!mx)
return -EINVAL;
- encode_trau_frame(trau_bits_out, tf);
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ /* set c-bits and t-bits */
+ tf.c_bits[0] = 1;
+ tf.c_bits[1] = 1;
+ tf.c_bits[2] = 1;
+ tf.c_bits[3] = 0;
+ tf.c_bits[4] = 0;
+ memset(&tf.c_bits[5], 0, 6);
+ memset(&tf.c_bits[11], 1, 10);
+ memset(&tf.t_bits[0], 1, 4);
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts input bits */
+ k = gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset output bits */
+ while(i < 260) {
+ tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
+ if (--k < 0) {
+ o += gsm_fr_map[l];
+ k = gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+ break;
+ default:
+ DEBUGPC(DMUX, "unsupported message type %d\n",
+ frame->msg_type);
+ return -EINVAL;
+ }
+
+ encode_trau_frame(trau_bits_out, &tf);
/* and send it to the muxer */
return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,