From jason.anoop at gmail.com Mon Dec 2 13:37:02 2013 From: jason.anoop at gmail.com (Jason Anoop) Date: Mon, 2 Dec 2013 19:07:02 +0530 Subject: Openbsc support for new BTS In-Reply-To: <20131108161746.GM5624@nataraja.gnumonks.org> References: <813F249CA3F4814F9249E5253FFA7CC2496EA33A@MAIL2.octasic.com> <20131107191528.GB5624@nataraja.gnumonks.org> <813F249CA3F4814F9249E5253FFA7CC24970648D@MAIL2.octasic.com> <20131108161746.GM5624@nataraja.gnumonks.org> Message-ID: Dear Harald, Sorry for the delay in response. I was busy with the interface design and coding. On Fri, Nov 8, 2013 at 9:47 PM, Harald Welte wrote: > So the opstrart for the RADIO_CARRIER is sent in response to an incoming > NM_MT_STATECHG_EVENT_REP withe the operational state set to DISABLED and > the availability set to OK. > My guess is that you are not sending such a state event from the BTS to > the BSC. You were just spot on Harald. That's working now. Many thanks. To update you further, My development has gone ahead for quite some time now. Now I'm able to configure TRX, configure TS and logical channels. RTS indications(for SDCCH), Time indications and DATA requests are being exchanged between osmobts - Octasic PHY. And finally now I'm able to see the network. So far so good. That said, I'm not able to latch to the network yet as I've not yet done with my coding. Also I'm yet to get my GSM sim cards for testing. Only then I will be able to test the rest (latching to the network and voice calls). Meantime I have some questions: (1) I'm not very clear on the usage of msgb data structure of libosmocore yet. Ex: what's the significance of l1h, l2h ptrs in msgb. l1p_msg->l2h = l1p_msg->data; l1p_msg->l1h = msgb_push(l1p_msg, sizeof(*l1sap)); I also see that l1h points to the msgb->data itself. And there are lots of typecasts happening as the L1 indications are being processed. As of now I just allocate memory for msgb, (1032 bytes) and then memcopy the L1 message to the msgb->data and set the msgb->data_len to the size of L1 msg & msgb->len to 1032 for mainly RTS ind & RACH ind and it works. I guess that's because maybe the msgb contents are not directly used as we extract important fields into l1sap datastructure. But I'm not too sure of that for DATA ind, for the voice traffic. I think I only need to send the MS data content from the data indications as I'm L1 header decoding is not possible at openbsc side as it is PHY dependent. Some pointers around here to make my understanding clear on what should be the message content for msgb or l1sap when it is sent up in the uplink direction in l1sap_up(), would be of great help. (2) In handle_ph_ra_ind() in l1_if.c od sysmobts, l1sap->u.rach_ind.chan_nr has not been filled up with 0x88, that means it should always call l1sap_handover_rach() in l1sap.c for every RACH for SDCCH. Is there any other way of distinguishing if it is HO ACCESS or normal ACCESS? (This was from code I downloaded, I don't know if this was fixed later??) (3) In the BSC config, encryption takes value of a5 0/1/2 Does 0 mean no encryption? If not how do I switch off encryption from openbsc? Just skip that line in config? (4) Regarding the sharing of code on my new interface: I'm yet to clean up my code and I think 1st release would be at least when I'm able to latch to the MS. I'm new to git repo, I will have to go through tutorials before I can create new repo for sharing this new interface with openbsc. Is there any central server where we can create such repo for openbsc? I prefer to create a new repo as adding it to existing branch may break other stuff of osmobts-trx and osmobts-sysmo. May be once I have stable release we can merge it up with main branch. Jason -------------- next part -------------- An HTML attachment was scrubbed... URL: From laforge at gnumonks.org Wed Dec 11 00:17:58 2013 From: laforge at gnumonks.org (Harald Welte) Date: Tue, 10 Dec 2013 19:17:58 -0500 Subject: Openbsc support for new BTS In-Reply-To: References: <813F249CA3F4814F9249E5253FFA7CC2496EA33A@MAIL2.octasic.com> <20131107191528.GB5624@nataraja.gnumonks.org> <813F249CA3F4814F9249E5253FFA7CC24970648D@MAIL2.octasic.com> <20131108161746.GM5624@nataraja.gnumonks.org> Message-ID: <20131211001758.GB7351@nataraja.gnumonks.org> Hi Jason, On Mon, Dec 02, 2013 at 07:07:02PM +0530, Jason Anoop wrote: > Sorry for the delay in response. I was busy with the interface design and > coding. I'm the one who took lots of time for the response this time :/ > My development has gone ahead for quite some time now. Now I'm able to > configure TRX, configure TS and logical channels. great. > I'm not very clear on the usage of msgb data structure of libosmocore yet. > Ex: what's the significance of l1h, l2h ptrs in msgb. they should be pointing to the layer1 / layer2 header of a message. What exactly is considered l1/l2/l3/... is dependent on the specific use case / interface. We use msgb's for all types of GSM and non-gesm interfaces, and their use is not always very consistent, sorry ;) 'msgb's are inspired by 'skb's (struct sk_buff) of the linux kernel, so if you read up on linux skbs, it will help you will understand the idea of msgbs. > l1p_msg->l2h = l1p_msg->data; this sents the layer2 pointer (lapdm) to the data pointer. > l1p_msg->l1h = msgb_push(l1p_msg, sizeof(*l1sap)); msgb_push is pushing (prepending) data to the buffer. Basically pre-pending the > I also see that l1h points to the msgb->data itself. And there are lots of > typecasts happening as the L1 indications are being processed. Those typecasts are likely hacks and should be revisited. Not every developer (myself included) is showing the required discipline to do things as they should be done. And the 'trx' branch you're working on is a particularly recent part of code (and not yet merged to master), so that might even show more of that. > As of now I just allocate memory for msgb, (1032 bytes) and then memcopy > the L1 message to the msgb->data and set the msgb->data_len to the size of > L1 msg & msgb->len to 1032 for mainly RTS ind & RACH ind and it works. I > guess that's because maybe the msgb contents are not directly used as we > extract important fields into l1sap datastructure. Normally we would attempt to avoid such extra copies by allocating a msgb and already reading the received l1 message (from a socket in your case?) into the msgb. We would then process and possibly strip the primitive header of your PHY and replace it with the primitive header the trx branch uses, push that further up into the generic part of osmo-bts, which then processes the layer2 payload in the LAPDm code. If it is not a a fragment that needs defragmentation, we would then push the RSL header in front and send the data out on the Abis interface without ever copying any of the data. The only copies that happen are from kernel->userspace (hidden in the read/recv system call from the PHY interface), and the second copy when writing to the TCP socket sending the RSL message. This is particularly important when looking at traffic channel data, of which there is much more than signalling frames. There's absolutely no need to copy that data again and again. > header decoding is not possible at openbsc side as it is PHY dependent. > Some pointers around here to make my understanding clear on what should be > the message content for msgb or l1sap when it is sent up in the uplink > direction in l1sap_up(), would be of great help. I hope the explanation above gives you a general idea. For any more detailed help, I would suggest publishing your patches / branch in a way that it can be reviewed. > In handle_ph_ra_ind() in l1_if.c od sysmobts, > l1sap->u.rach_ind.chan_nr has not been filled up with 0x88, that means > it should always call l1sap_handover_rach() in l1sap.c for every RACH > for SDCCH. Is there any other way of distinguishing if it is HO ACCESS > or normal ACCESS? the channel number will indicate if the RACH was received on an uplink CCCH (RACH channel), or on a SDCCH or TCH. In the former case it is normal access, in the latter case it must be handover access. > In the BSC config, encryption takes value of a5 0/1/2 > Does 0 mean no encryption? If not how do I switch off encryption from > openbsc? Just skip that line in config? A5/0 is specified as no encryption in the GSM specification, yes. That's not OpenBSC specific. > Regarding the sharing of code on my new interface: > I'm yet to clean up my code and I think 1st release would be at least when > I'm able to latch to the MS. Don't think in terms of releases. Have you seen any OsmoBTS releases anywhere? There are none. Whatever any of our develoeprs works on, is pushed to a branch in the repository. Even if its not finished yet, sometimes even before it compiles. This is one way to get more people to look at the code, before it gets too large/complex for anyone to review ;) You're not delivering code to a customer, but you are sharing it with other developers. > I'm new to git repo, I will have to go through tutorials before I can > create new repo for sharing this new interface with openbsc. > Is there any central server where we can create such repo for openbsc? OpenBSC should not really need any modifications, at least from what I can tell. Assuming you meant OsmoBTS, then yes, we have git.osmocom.org on which we can create new repositories - or of course branches to existing repositories. > I prefer to create a new repo as adding it to existing branch may break > other stuff of osmobts-trx and osmobts-sysmo. The normal way we handle this is by having an additional branch on top of the branch you forked from. A completely new repository isn't really needed, just one more (or N more) new branches. The only part where you have to be careful is to never push to master or somebody elses branch, but only push to your personal branches. > May be once I have stable release we can merge it up with main branch. We'll see, code normally receives some review, and if you're unfamiliar with the coding style and customs of the project, then typically experience shows that the code will have to go through a few revisisons before it can be merged. What's also important is to have self-contained commits with proper commit messages. Each logical change (like addition of an new feature, fixing one bug) should be a separate commit. Regards, Harald -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From laforge at gnumonks.org Tue Dec 31 14:28:05 2013 From: laforge at gnumonks.org (Harald Welte) Date: Tue, 31 Dec 2013 15:28:05 +0100 Subject: OsmoDevCon 2014 / Date / Venue In-Reply-To: <20131130135720.GI3133@nataraja.gnumonks.org> References: <20131105155825.GK12353@nataraja.gnumonks.org> <20131130135720.GI3133@nataraja.gnumonks.org> Message-ID: <20131231142805.GM13435@nataraja.gnumonks.org> Dear all, just a quick reminder: On Sat, Nov 30, 2013 at 02:57:20PM +0100, Harald Welte wrote: > Please make sure to add your name to the list at > https://openbsc.osmocom.org/trac/wiki/OsmoDevCon2014 until December > 31st (latest). If you don't have a wiki account, ask somebody who has > one, apply for a wiki account or send an e-mail to me. so today is the last chance to indicate your intrest in attending OsmoDevCon. I'm surprised to not have seen the following names in the list: pablo, peter, nion, steve-m, dmitry, kaber. It would be great to have you around again this year. Looking forward to meeting all of you again! Regards, Harald -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From kaber at trash.net Tue Dec 31 14:50:03 2013 From: kaber at trash.net (Patrick McHardy) Date: Tue, 31 Dec 2013 14:50:03 +0000 Subject: OsmoDevCon 2014 / Date / Venue In-Reply-To: <20131231142805.GM13435@nataraja.gnumonks.org> References: <20131105155825.GK12353@nataraja.gnumonks.org> <20131130135720.GI3133@nataraja.gnumonks.org> <20131231142805.GM13435@nataraja.gnumonks.org> Message-ID: <134d1fe1-302d-4782-974f-477a8762f27f@email.android.com> Hi, Sorry I didn't see the invitation before. I'm happy to come. Cheers & happy New year, Patrick Harald Welte schrieb: >Dear all, > >just a quick reminder: > >On Sat, Nov 30, 2013 at 02:57:20PM +0100, Harald Welte wrote: >> Please make sure to add your name to the list at >> https://openbsc.osmocom.org/trac/wiki/OsmoDevCon2014 until December >> 31st (latest). If you don't have a wiki account, ask somebody who >has >> one, apply for a wiki account or send an e-mail to me. > >so today is the last chance to indicate your intrest in attending >OsmoDevCon. > >I'm surprised to not have seen the following names in the list: pablo, >peter, nion, steve-m, dmitry, kaber. It would be great to have you >around again this year. > >Looking forward to meeting all of you again! > >Regards, > Harald >-- >- Harald Welte >http://laforge.gnumonks.org/ >============================================================================ >"Privacy in residential applications is a desirable marketing option." > (ETSI EN 300 175-7 Ch. A6) -- Diese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet. -------------- next part -------------- An HTML attachment was scrubbed... URL: From Max.Suraev at fairwaves.ru Fri Dec 27 22:02:40 2013 From: Max.Suraev at fairwaves.ru (=?UTF-8?B?4piO?=) Date: Fri, 27 Dec 2013 23:02:40 +0100 Subject: [PATCH] Use generic osmocom auth api In-Reply-To: References: <528B57B5.7080400@fairwaves.ru> <20131122160729.GM18191@nataraja.gnumonks.org> <528F8791.20405@fairwaves.ru> Message-ID: <52BDF900.9020901@fairwaves.ru> Here it is - rely on libosmocore to do the right stuff for us. -- best regards, Max, http://fairwaves.ru From max.suraev at fairwaves.ru Fri Dec 27 21:58:45 2013 From: max.suraev at fairwaves.ru (Max) Date: Fri, 27 Dec 2013 22:58:45 +0100 Subject: [PATCH] Use generic osmocom auth API Message-ID: --- openbsc/src/libmsc/auth.c | 69 +++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c index 10d8edf..d04b2fc 100644 --- a/openbsc/src/libmsc/auth.c +++ b/openbsc/src/libmsc/auth.c @@ -24,47 +24,11 @@ #include #include #include - -#include +#include #include -static int -_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - int i, l = ainfo->a3a8_ki_len; - - if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) { - LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n", - ainfo->a3a8_ki_len, - osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - for (i=0; i<4; i++) - atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; - for (i=4; i<12; i++) - atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; - - return 0; -} - -static int -_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) { - LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", - ainfo->a3a8_ki_len, - osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc); - - return 0; -} - /* Return values * -1 -> Internal error * 0 -> Not available @@ -76,6 +40,11 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, { struct gsm_auth_info ainfo; int i, rc; + static struct osmo_sub_auth_data auth = { + .type = OSMO_AUTH_TYPE_GSM + }; + struct osmo_auth_vector _vec; + struct osmo_auth_vector *vec = &_vec; /* Get subscriber info (if any) */ rc = db_get_authinfo_for_subscr(&ainfo, subscr); @@ -109,13 +78,23 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, return 0; case AUTH_ALGO_XOR: - if (_use_xor(&ainfo, atuple)) - return 0; + auth.algo = OSMO_AUTH_ALG_XOR; + if ((ainfo.a3a8_ki_len > A38_XOR_MAX_KEY_LEN) || (ainfo.a3a8_ki_len < A38_XOR_MIN_KEY_LEN)) { + LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n", + ainfo.a3a8_ki_len, + osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len)); + return -1; + } break; case AUTH_ALGO_COMP128v1: - if (_use_comp128_v1(&ainfo, atuple)) - return 0; + auth.algo = OSMO_AUTH_ALG_COMP128v1; + if (ainfo.a3a8_ki_len != A38_COMP128_KEY_LEN) { + LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", + ainfo.a3a8_ki_len, + osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len)); + return -1; + } break; default: @@ -124,6 +103,14 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, return 0; } + memcpy(auth.u.gsm.ki, ainfo.a3a8_ki, sizeof(auth.u.gsm.ki)); + + if (osmo_auth_gen_vec(vec, &auth, atuple->rand) < 0) + return -1; + + memcpy(atuple->sres, vec->sres, 4); + memcpy(atuple->kc, vec->kc, 8); + db_sync_lastauthtuple_for_subscr(atuple, subscr); DEBUGP(DMM, "Need to do authentication and ciphering\n"); -- 1.8.3.2 --------------040803050908070700040406-- From max.suraev at fairwaves.ru Fri Dec 27 21:58:45 2013 From: max.suraev at fairwaves.ru (Max) Date: Fri, 27 Dec 2013 22:58:45 +0100 Subject: [PATCH] Use generic osmocom auth API Message-ID: --- openbsc/src/libmsc/auth.c | 69 +++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c index 10d8edf..d04b2fc 100644 --- a/openbsc/src/libmsc/auth.c +++ b/openbsc/src/libmsc/auth.c @@ -24,47 +24,11 @@ #include #include #include - -#include +#include #include -static int -_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - int i, l = ainfo->a3a8_ki_len; - - if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) { - LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n", - ainfo->a3a8_ki_len, - osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - for (i=0; i<4; i++) - atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; - for (i=4; i<12; i++) - atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; - - return 0; -} - -static int -_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) { - LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", - ainfo->a3a8_ki_len, - osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc); - - return 0; -} - /* Return values * -1 -> Internal error * 0 -> Not available @@ -76,6 +40,11 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, { struct gsm_auth_info ainfo; int i, rc; + static struct osmo_sub_auth_data auth = { + .type = OSMO_AUTH_TYPE_GSM + }; + struct osmo_auth_vector _vec; + struct osmo_auth_vector *vec = &_vec; /* Get subscriber info (if any) */ rc = db_get_authinfo_for_subscr(&ainfo, subscr); @@ -109,13 +78,23 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, return 0; case AUTH_ALGO_XOR: - if (_use_xor(&ainfo, atuple)) - return 0; + auth.algo = OSMO_AUTH_ALG_XOR; + if ((ainfo.a3a8_ki_len > A38_XOR_MAX_KEY_LEN) || (ainfo.a3a8_ki_len < A38_XOR_MIN_KEY_LEN)) { + LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n", + ainfo.a3a8_ki_len, + osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len)); + return -1; + } break; case AUTH_ALGO_COMP128v1: - if (_use_comp128_v1(&ainfo, atuple)) - return 0; + auth.algo = OSMO_AUTH_ALG_COMP128v1; + if (ainfo.a3a8_ki_len != A38_COMP128_KEY_LEN) { + LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", + ainfo.a3a8_ki_len, + osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len)); + return -1; + } break; default: @@ -124,6 +103,14 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, return 0; } + memcpy(auth.u.gsm.ki, ainfo.a3a8_ki, sizeof(auth.u.gsm.ki)); + + if (osmo_auth_gen_vec(vec, &auth, atuple->rand) < 0) + return -1; + + memcpy(atuple->sres, vec->sres, 4); + memcpy(atuple->kc, vec->kc, 8); + db_sync_lastauthtuple_for_subscr(atuple, subscr); DEBUGP(DMM, "Need to do authentication and ciphering\n"); -- 1.8.3.2 --------------000607000700020905030202-- From jerlbeck at sysmocom.de Mon Dec 2 09:21:58 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Mon, 02 Dec 2013 10:21:58 +0100 Subject: [PATCH 5/8] mgcp: NUL-terminate MGCP message In-Reply-To: <20131130181622.GF5460@xiaoyu.lan> References: <1385729030-5155-1-git-send-email-jerlbeck@sysmocom.de> <1385729030-5155-5-git-send-email-jerlbeck@sysmocom.de> <20131130181622.GF5460@xiaoyu.lan> Message-ID: <529C5136.4020606@sysmocom.de> On 11/30/2013 07:16 PM, Holger Hans Peter Freyther wrote: > On Fri, Nov 29, 2013 at 01:43:47PM +0100, Jacob Erlbeck wrote: >> The MGCP message isn't always NUL-terminated when arriving at >> mgcp_handle_message(). This may lead to undefined results. > > oh! > >> + /* Ensure that the msg->l2h is NULL terminated. */ >> + if (msgb_tailroom(msg) > 0) >> + *msg->tail = '\0'; >> + else if (*(msg->tail-1) == '\r' || *(msg->tail-1) == '\n') >> + *(msg->tail - 1) = '\0'; >> + else { >> + LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " >> + "Length: %d, Buffer size: %d\n", >> + msgb_l2len(msg), msg->data_len); >> + return NULL; >> + } > > The check misses if "tail - 1" is already \0 Yes, but does this hurt? I wouldn't expect l2 to end with a \0 since the protocol is defined without one (see below). And even if there was one, a second one wouldn't be a problem. > and if tail - 1 is not NULL. You mean that tail == (uint8_t *)1 ??? This mustn't happen anyway. > I would just add an OSMO_ASSERT and fix the caller that didn't > null terminate?! What do you think? I don't agree that the caller has to NUL-terminate. It is an implementation detail of mgcp_handle_message() that it uses string parsing functions that depend on a terminating \0 character. The caller provides the length of the data area and that just fine. Even more it reserves a byte at the end of the buffer to have space for a \0, which is more than the protocol requires. And it guarantees this way, that the first condition (tailroom > 0) always succeeds, which is much better IMO than inserting protocol violating garbage characters (the \0) at the end of the buffer within the l2 data area. What's about changing msgb to always write a \0 after the data area every time tail gets modified? Like with QByteArray you could always safely use string functions without handling this explicitely. But I'm afraid there is some code already that relies of changing tail freely without expecting this to cause this kind of side effect even when used with put. Perhaps one can make this optional? Jacob From holger at freyther.de Mon Dec 2 10:14:43 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Mon, 2 Dec 2013 11:14:43 +0100 Subject: [PATCH 5/8] mgcp: NUL-terminate MGCP message In-Reply-To: <529C5136.4020606@sysmocom.de> References: <1385729030-5155-1-git-send-email-jerlbeck@sysmocom.de> <1385729030-5155-5-git-send-email-jerlbeck@sysmocom.de> <20131130181622.GF5460@xiaoyu.lan> <529C5136.4020606@sysmocom.de> Message-ID: <20131202101443.GC2877@xiaoyu.lan> On Mon, Dec 02, 2013 at 10:21:58AM +0100, Jacob Erlbeck wrote: > >> + /* Ensure that the msg->l2h is NULL terminated. */ > >> + if (msgb_tailroom(msg) > 0) > >> + *msg->tail = '\0'; > >> + else if (*(msg->tail-1) == '\r' || *(msg->tail-1) == '\n') > >> + *(msg->tail - 1) = '\0'; > >> + else { > >> + return NULL; > >> + } > > > > The check misses if "tail - 1" is already \0 > Yes, but does this hurt? I wouldn't expect l2 to end with a \0 since the > protocol is defined without one (see below). And even if there was one, > a second one wouldn't be a problem. tailroom == 0 AND msg->l2h[msgb_length(msg) - 1] == '\0' is rejected by your above code. > You mean that tail == (uint8_t *)1 ??? This mustn't happen anyway. true. I had tail not being initialized in mind. But that can not happen. > I don't agree that the caller has to NUL-terminate. It is an > implementation detail of mgcp_handle_message() that it uses string > parsing functions that depend on a terminating \0 character. The Fair enough. I just noticed that the check above is a bit of code and I think it missed one case, so a OSMO_ASSERT is easier to get right. > What's about changing msgb to always write a \0 after the data area > every time tail gets modified? Like with QByteArray you could always > safely use string functions without handling this explicitely. But I'm > afraid there is some code already that relies of changing tail freely > without expecting this to cause this kind of side effect even when used > with put. Perhaps one can make this optional? In theory we always over allocate the msgb and due the usage of talloc_zero should make sure that we have many NULLs at the end. If we want to implement the extra reservation we have a bit of a problem. We generally use a power of two for the allocation (in the hope we get a full page). So if we do: +1: We certainly need to allocate more than a page -1: We might not have enough data. For now. Please change the check to use l2h, msgb_length and handle a NULL terminated input with no tailroom. kind regards holger From jerlbeck at sysmocom.de Mon Dec 2 10:38:27 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Mon, 02 Dec 2013 11:38:27 +0100 Subject: [PATCH 5/8] mgcp: NUL-terminate MGCP message In-Reply-To: <20131202101443.GC2877@xiaoyu.lan> References: <1385729030-5155-1-git-send-email-jerlbeck@sysmocom.de> <1385729030-5155-5-git-send-email-jerlbeck@sysmocom.de> <20131130181622.GF5460@xiaoyu.lan> <529C5136.4020606@sysmocom.de> <20131202101443.GC2877@xiaoyu.lan> Message-ID: <529C6323.3050603@sysmocom.de> On 12/02/2013 11:14 AM, Holger Hans Peter Freyther wrote: > On Mon, Dec 02, 2013 at 10:21:58AM +0100, Jacob Erlbeck wrote: > >>>> + /* Ensure that the msg->l2h is NULL terminated. */ >>>> + if (msgb_tailroom(msg) > 0) >>>> + *msg->tail = '\0'; >>>> + else if (*(msg->tail-1) == '\r' || *(msg->tail-1) == '\n') >>>> + *(msg->tail - 1) = '\0'; >>>> + else { >>>> + return NULL; >>>> + } >>> >>> The check misses if "tail - 1" is already \0 >> Yes, but does this hurt? I wouldn't expect l2 to end with a \0 since the >> protocol is defined without one (see below). And even if there was one, >> a second one wouldn't be a problem. > > tailroom == 0 AND msg->l2h[msgb_length(msg) - 1] == '\0' is rejected > by your above code. Ok, agreed. And to be complete, the test of the length of the data arae being > 0 is missing, too. So I'll add sth like: else if (msgb_l2len(msg) > 0 && *(msg->tail-1) == '\0') /* do nothing */ else ... '\r' ... Or do you rather recommend to use msg->l2h + msgb_l2len(msg) instead of msg->tail directly? And why do you use msgb_length() and not msgb_l2len()? Jacob From holger at freyther.de Mon Dec 2 11:13:57 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Mon, 2 Dec 2013 12:13:57 +0100 Subject: [PATCH 5/8] mgcp: NUL-terminate MGCP message In-Reply-To: <529C6323.3050603@sysmocom.de> References: <1385729030-5155-1-git-send-email-jerlbeck@sysmocom.de> <1385729030-5155-5-git-send-email-jerlbeck@sysmocom.de> <20131130181622.GF5460@xiaoyu.lan> <529C5136.4020606@sysmocom.de> <20131202101443.GC2877@xiaoyu.lan> <529C6323.3050603@sysmocom.de> Message-ID: <20131202111357.GG2877@xiaoyu.lan> On Mon, Dec 02, 2013 at 11:38:27AM +0100, Jacob Erlbeck wrote: > Or do you rather recommend to use msg->l2h + msgb_l2len(msg) instead of > msg->tail directly? I think using msg->l2h + msgb_l2len is the correct thing to use. There could be other information in the message (yes, I strtok and other things I did to the message would break things) > And why do you use msgb_length() and not msgb_l2len()? Please us l2len. My example was wrong. Thanks for noticing that! From jerlbeck at sysmocom.de Mon Dec 2 10:47:38 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Mon, 02 Dec 2013 11:47:38 +0100 Subject: [PATCH 5/8] mgcp: NUL-terminate MGCP message In-Reply-To: <20131202101443.GC2877@xiaoyu.lan> References: <1385729030-5155-1-git-send-email-jerlbeck@sysmocom.de> <1385729030-5155-5-git-send-email-jerlbeck@sysmocom.de> <20131130181622.GF5460@xiaoyu.lan> <529C5136.4020606@sysmocom.de> <20131202101443.GC2877@xiaoyu.lan> Message-ID: <529C654A.7030301@sysmocom.de> On 12/02/2013 11:14 AM, Holger Hans Peter Freyther wrote: > On Mon, Dec 02, 2013 at 10:21:58AM +0100, Jacob Erlbeck wrote: > > >> What's about changing msgb to always write a \0 after the data area >> every time tail gets modified? Like with QByteArray you could always >> safely use string functions without handling this explicitely. But I'm >> afraid there is some code already that relies of changing tail freely >> without expecting this to cause this kind of side effect even when used >> with put. Perhaps one can make this optional? > > In theory we always over allocate the msgb and due the usage of > talloc_zero should make sure that we have many NULLs at the end. The other problem here is, that the buffer gets reused for each MGCP message, so we always have NUL-terminated strings (which made valgrind at al happy all the time) but occasionally with garbage at the end. Jacob From jerlbeck at sysmocom.de Tue Dec 3 16:14:44 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 3 Dec 2013 17:14:44 +0100 Subject: [PATCH] mgcp: NUL-terminate MGCP message In-Reply-To: <1385729030-5155-5-git-send-email-jerlbeck@sysmocom.de> References: <1385729030-5155-5-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386087284-11397-1-git-send-email-jerlbeck@sysmocom.de> The MGCP message isn't always NUL-terminated when arriving at mgcp_handle_message(). This may lead to undefined results. This patch ensures that the message text is NUL-terminated by setting *msg->tail to '\0' in mgcp_handle_message(). Addresses: <000b> mgcp_protocol.c:642 Unhandled option: 'r'/114 on 0x3 <000b> mgcp_protocol.c:593 Unhandled SDP option: '='/61 on 0x3 <000b> mgcp_protocol.c:871 Unhandled option: '.'/46 on 0x2 Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_protocol.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index d4a23a7..3487410 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -261,6 +261,19 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) int i, code, handled = 0; struct msgb *resp = NULL; char *data; + unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ + + /* Ensure that the msg->l2h is NULL terminated. */ + if (msgb_tailroom(msg) > 0) + *tail = '\0'; + else if (msgb_l2len(msg) > 0 && (tail[-1] == '\r' || tail[-1] == '\n')) + tail[-1] = '\0'; + else { + LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " + "Length: %d, Buffer size: %d\n", + msgb_l2len(msg), msg->data_len); + return NULL; + } if (msgb_l2len(msg) < 4) { LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len); @@ -278,7 +291,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) /* * Check for a duplicate message and respond. - * FIXME: Verify that the msg->l3h is NULL terminated. */ memset(&pdata, 0, sizeof(pdata)); pdata.cfg = cfg; -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 3 16:17:04 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 3 Dec 2013 17:17:04 +0100 Subject: [PATCH 1/6] mgcp/rtp: Add more test cases for RTP header patching Message-ID: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> This patch extends the existing RTP error check test by adding support for selectively enabling/disabling SSRC and timestamp patching. The test is repeated for all combinations thereof. Sponsored-by: On-Waves ehf --- openbsc/tests/mgcp/mgcp_test.c | 13 +++++++---- openbsc/tests/mgcp/mgcp_test.ok | 48 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index c58f52d..4326738 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -516,7 +516,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, char *data, int len); -static void test_packet_error_detection(void) +static void test_packet_error_detection(int patch_ssrc, int patch_ts) { int i; @@ -527,7 +527,9 @@ static void test_packet_error_detection(void) struct sockaddr_in addr = {0}; char buffer[4096]; - printf("Testing packet error detection.\n"); + printf("Testing packet error detection%s%s.\n", + patch_ssrc ? ", patch SSRC" : "", + patch_ts ? ", patch timestamps" : ""); memset(&trunk, 0, sizeof(trunk)); memset(&endp, 0, sizeof(endp)); @@ -545,7 +547,7 @@ static void test_packet_error_detection(void) mgcp_free_endp(&endp); rtp->payload_type = 98; - endp.allow_patch = 1; + endp.allow_patch = patch_ssrc; for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { struct rtp_packet_info *info = test_rtp_packets1 + i; @@ -575,7 +577,10 @@ int main(int argc, char **argv) test_packet_loss_calc(); test_rqnt_cb(); test_mgcp_stats(); - test_packet_error_detection(); + test_packet_error_detection(1, 0); + test_packet_error_detection(0, 0); + test_packet_error_detection(0, 1); + test_packet_error_detection(1, 1); printf("Done\n"); return EXIT_SUCCESS; diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 3bfd78b..71293ad 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -42,6 +42,22 @@ Testing packet loss calculation. Testing stat parsing Parsing result: 0 Parsing result: 0 +Testing packet error detection, patch SSRC. +TS: 0, dTS: 0, TS Errs: in 0, out 0 +TS: 160, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 1, out 1 +TS: 480, dTS: 160, TS Errs: in 1, out 1 +TS: 640, dTS: 160, TS Errs: in 1, out 1 +TS: 960, dTS: 320, TS Errs: in 2, out 2 +TS: 1120, dTS: 160, TS Errs: in 3, out 3 +TS: 1280, dTS: 160, TS Errs: in 3, out 3 +TS: 1400, dTS: 120, TS Errs: in 4, out 4 +TS: 1560, dTS: 160, TS Errs: in 5, out 5 +TS: 1720, dTS: 160, TS Errs: in 5, out 5 +TS: 1880, dTS: 160, TS Errs: in 5, out 5 +TS: 2040, dTS: 160, TS Errs: in 5, out 5 +TS: 2200, dTS: 160, TS Errs: in 5, out 5 Testing packet error detection. TS: 0, dTS: 0, TS Errs: in 0, out 0 TS: 160, dTS: 160, TS Errs: in 0, out 0 @@ -55,6 +71,38 @@ TS: 1280, dTS: 160, TS Errs: in 3, out 3 TS: 1400, dTS: 120, TS Errs: in 4, out 4 TS: 1560, dTS: 160, TS Errs: in 5, out 5 TS: 1720, dTS: 160, TS Errs: in 5, out 5 +TS: 34688, dTS: 160, TS Errs: in 5, out 5 +TS: 34848, dTS: 160, TS Errs: in 5, out 5 +TS: 35008, dTS: 160, TS Errs: in 5, out 5 +Testing packet error detection, patch timestamps. +TS: 0, dTS: 0, TS Errs: in 0, out 0 +TS: 160, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 1, out 1 +TS: 480, dTS: 160, TS Errs: in 1, out 1 +TS: 640, dTS: 160, TS Errs: in 1, out 1 +TS: 960, dTS: 320, TS Errs: in 2, out 2 +TS: 1120, dTS: 160, TS Errs: in 3, out 3 +TS: 1280, dTS: 160, TS Errs: in 3, out 3 +TS: 1400, dTS: 120, TS Errs: in 4, out 4 +TS: 1560, dTS: 160, TS Errs: in 5, out 5 +TS: 1720, dTS: 160, TS Errs: in 5, out 5 +TS: 34688, dTS: 160, TS Errs: in 5, out 5 +TS: 34848, dTS: 160, TS Errs: in 5, out 5 +TS: 35008, dTS: 160, TS Errs: in 5, out 5 +Testing packet error detection, patch SSRC, patch timestamps. +TS: 0, dTS: 0, TS Errs: in 0, out 0 +TS: 160, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 1, out 1 +TS: 480, dTS: 160, TS Errs: in 1, out 1 +TS: 640, dTS: 160, TS Errs: in 1, out 1 +TS: 960, dTS: 320, TS Errs: in 2, out 2 +TS: 1120, dTS: 160, TS Errs: in 3, out 3 +TS: 1280, dTS: 160, TS Errs: in 3, out 3 +TS: 1400, dTS: 120, TS Errs: in 4, out 4 +TS: 1560, dTS: 160, TS Errs: in 5, out 5 +TS: 1720, dTS: 160, TS Errs: in 5, out 5 TS: 1880, dTS: 160, TS Errs: in 5, out 5 TS: 2040, dTS: 160, TS Errs: in 5, out 5 TS: 2200, dTS: 160, TS Errs: in 5, out 5 -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 3 16:17:05 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 3 Dec 2013 17:17:05 +0100 Subject: [PATCH 2/6] mgcp/rtp: Fix output timing error counter In-Reply-To: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386087429-11618-2-git-send-email-jerlbeck@sysmocom.de> The tsdelta computation and error detection didn't handle the intialisation phase properly. This patches fixes this by skipping the output timing validation when the SSRCs don't match. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 75d39c1..06452a6 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -237,6 +237,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->jitter = 0; state->transit = arrival_time - timestamp; state->out_stream = state->in_stream; + state->out_stream.last_timestamp = timestamp; + state->out_stream.ssrc = rtp_hdr->ssrc - 1; /* SSRC change <=> initialisation */ + LOGP(DMGCP, LOGL_INFO, + "Initializing stream on 0x%x SSRC: %u timestamp: %u " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + state->seq_offset, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); } else if (state->in_stream.ssrc != rtp_hdr->ssrc) { int32_t tsdelta = state->out_stream.last_tsdelta; if (tsdelta == 0) { @@ -286,9 +295,10 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta } /* Check again, whether the timestamps are still valid */ - check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr, - seq, timestamp, "output", - &state->out_stream.last_tsdelta); + if (state->out_stream.ssrc == rtp_hdr->ssrc) + check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr, + seq, timestamp, "output", + &state->out_stream.last_tsdelta); /* * The below takes the shape of the validation from Appendix A. Check -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 3 16:17:06 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 3 Dec 2013 17:17:06 +0100 Subject: [PATCH 3/6] mgcp/rtp: Add RTP header patch mode configuration In-Reply-To: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386087429-11618-3-git-send-email-jerlbeck@sysmocom.de> This adds datastructures and a VTY frontend to configure the different type of RTP header patching: SSRC and timestamp. Note that timestamp patching is not yet implemented. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp.h | 4 +++ openbsc/include/openbsc/mgcp_internal.h | 10 ++++-- openbsc/src/libmgcp/mgcp_network.c | 2 +- openbsc/src/libmgcp/mgcp_protocol.c | 12 ++++++- openbsc/src/libmgcp/mgcp_vty.c | 56 ++++++++++++++++++++++++++++++- openbsc/tests/mgcp/mgcp_test.c | 6 +++- 6 files changed, 83 insertions(+), 7 deletions(-) diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 4e22e0f..8ab52ce 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -118,6 +118,10 @@ struct mgcp_trunk_config { int omit_rtcp; + /* RTP patching */ + int force_constant_ssrc; /* 0: don't, 1: once */ + int force_constant_timing; + /* spec handling */ int force_realloc; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 0b52c1c..02e193d 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -83,6 +83,10 @@ struct mgcp_rtp_end { int frames_per_packet; char *fmtp_extra; + /* RTP patching */ + int force_constant_ssrc; + int force_constant_timing; + /* * Each end has a socket... */ @@ -142,9 +146,6 @@ struct mgcp_endpoint { struct mgcp_rtp_state net_state; struct mgcp_rtp_state bts_state; - /* SSRC/seq/ts patching for loop */ - int allow_patch; - /* fields for re-transmission */ char *last_trans; char *last_response; @@ -176,6 +177,9 @@ static inline int endp_back_channel(int endpoint) struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); +void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, + struct mgcp_rtp_end *rtp); + void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, uint32_t *expected, int *loss); uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *); diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 06452a6..ce4e1d3 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -263,7 +263,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->seq_offset = (state->out_stream.last_seq + 1) - seq; state->timestamp_offset = (state->out_stream.last_timestamp + tsdelta) - timestamp; - state->patch = endp->allow_patch; + state->patch = rtp_end->force_constant_ssrc; LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed on 0x%x SSRC: %u offset: %d tsdelta: %d " "from %s:%d in %d\n", diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index f09058d..6d31ace 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -612,6 +612,15 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) return found_media; } +void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, + struct mgcp_rtp_end *rtp) +{ + struct mgcp_trunk_config *tcfg = endp->tcfg; + + rtp->force_constant_timing = tcfg->force_constant_timing; + rtp->force_constant_ssrc = tcfg->force_constant_ssrc; +} + static struct msgb *handle_create_con(struct mgcp_parse_data *p) { struct mgcp_trunk_config *tcfg; @@ -686,6 +695,8 @@ mgcp_header_done: /* initialize */ endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0; + mgcp_rtp_end_config(endp, 0, &endp->net_end); + mgcp_rtp_end_config(endp, 0, &endp->bts_end); /* set to zero until we get the info */ memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr)); @@ -1119,7 +1130,6 @@ void mgcp_free_endp(struct mgcp_endpoint *endp) memset(&endp->bts_state, 0, sizeof(endp->bts_state)); endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE; - endp->allow_patch = 0; memset(&endp->taps, 0, sizeof(endp->taps)); } diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 5aeb393..16b973f 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -31,6 +31,7 @@ #include #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n" +#define RTP_PATCH_STR "Modify RTP packet header in both directions\n" static struct mgcp_config *g_cfg = NULL; @@ -511,6 +512,13 @@ static int config_write_trunk(struct vty *vty) vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); else vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); + if (trunk->force_constant_ssrc || trunk->force_constant_timing) { + vty_out(vty, " rtp-patch ssrc %d%s", + trunk->force_constant_ssrc, VTY_NEWLINE); + vty_out(vty, " rtp-patch timestamp %d%s", + trunk->force_constant_timing, VTY_NEWLINE); + } else + vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); if (trunk->audio_fmtp_extra) vty_out(vty, " sdp audio fmtp-extra %s%s", trunk->audio_fmtp_extra, VTY_NEWLINE); @@ -599,6 +607,46 @@ DEFUN(cfg_trunk_no_omit_rtcp, return CMD_SUCCESS; } +DEFUN(cfg_trunk_patch_rtp_ssrc, + cfg_trunk_patch_rtp_ssrc_cmd, + "rtp-patch ssrc (0|1)", + RTP_PATCH_STR + "Force a fixed SSRC\n" + "Disable\n" + "Enable\n" + ) +{ + struct mgcp_trunk_config *trunk = vty->index; + trunk->force_constant_ssrc = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_patch_rtp_ts, + cfg_trunk_patch_rtp_ts_cmd, + "rtp-patch timestamp (0|1)", + RTP_PATCH_STR + "Adjust RTP timestamp\n" + "Disable\n" + "Enable\n" + ) +{ + struct mgcp_trunk_config *trunk = vty->index; + trunk->force_constant_timing = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_no_patch_rtp, + cfg_trunk_no_patch_rtp_cmd, + "no rtp-patch", + NO_STR RTP_PATCH_STR) +{ + struct mgcp_trunk_config *trunk = vty->index; + trunk->force_constant_ssrc = 0; + trunk->force_constant_timing = 0; + return CMD_SUCCESS; +} + + DEFUN(loop_endp, loop_endp_cmd, "loop-endpoint <0-64> NAME (0|1)", @@ -636,7 +684,10 @@ DEFUN(loop_endp, endp->conn_mode = MGCP_CONN_LOOPBACK; else endp->conn_mode = endp->orig_mode; - endp->allow_patch = 1; + + /* Handle it like a MDCX, switch on SSRC patching if enabled */ + mgcp_rtp_end_config(endp, 1, &endp->bts_end); + mgcp_rtp_end_config(endp, 1, &endp->net_end); return CMD_SUCCESS; } @@ -839,6 +890,9 @@ int mgcp_vty_init(void) install_element(TRUNK_NODE, &cfg_trunk_loop_cmd); install_element(TRUNK_NODE, &cfg_trunk_omit_rtcp_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_omit_rtcp_cmd); + install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd); + install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd); + install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd); install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd); return 0; diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 4326738..8c292f1 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -537,6 +537,9 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) trunk.number_endpoints = 1; trunk.endpoints = &endp; + trunk.force_constant_ssrc = patch_ssrc; + trunk.force_constant_timing = patch_ts; + endp.tcfg = &trunk; /* This doesn't free endp but resets/frees all fields of the structure @@ -547,7 +550,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) mgcp_free_endp(&endp); rtp->payload_type = 98; - endp.allow_patch = patch_ssrc; for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { struct rtp_packet_info *info = test_rtp_packets1 + i; @@ -556,6 +558,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) OSMO_ASSERT(info->len >= 0); memmove(buffer, info->data, info->len); + mgcp_rtp_end_config(&endp, 1, rtp); + mgcp_patch_and_count(&endp, &state, rtp, &addr, buffer, info->len); -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 3 16:17:07 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 3 Dec 2013 17:17:07 +0100 Subject: [PATCH 4/6] mgcp/rtp: Cleanup usage of variables in mgcp_patch_and_count() In-Reply-To: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386087429-11618-4-git-send-email-jerlbeck@sysmocom.de> The ssrc has been used without respect to proper byte ordering. The timestap_offset and seq_offset variables has been set even when patching was not allowed. This patch introduces a new variable 'ssrc' that takes the value of the SSRC in proper byte order. The offsets in the state are only updated if SSRC patching is allowed. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 1 + openbsc/src/libmgcp/mgcp_network.c | 73 ++++++++++++++++++++----------- openbsc/src/libmgcp/mgcp_protocol.c | 3 ++ 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 02e193d..b16bd49 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -59,6 +59,7 @@ struct mgcp_rtp_state { int cycles; int32_t timestamp_offset; + uint32_t packet_duration; uint32_t jitter; int32_t transit; diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index ce4e1d3..137ddba 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -216,7 +216,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta uint32_t arrival_time; int32_t transit, d; uint16_t seq, udelta; - uint32_t timestamp; + uint32_t timestamp, ssrc; struct rtp_hdr *rtp_hdr; int payload = rtp_end->payload_type; @@ -227,31 +227,43 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta seq = ntohs(rtp_hdr->sequence); timestamp = ntohl(rtp_hdr->timestamp); arrival_time = get_current_ts(); + ssrc = ntohl(rtp_hdr->ssrc); if (!state->initialized) { state->in_stream.last_seq = seq - 1; - state->in_stream.ssrc = state->orig_ssrc = rtp_hdr->ssrc; + state->in_stream.ssrc = state->orig_ssrc = ssrc; state->in_stream.last_tsdelta = 0; state->base_seq = seq; state->initialized = 1; state->jitter = 0; state->transit = arrival_time - timestamp; + state->packet_duration = + rtp_end->rate * rtp_end->frames_per_packet * + rtp_end->frame_duration_num / + rtp_end->frame_duration_den; state->out_stream = state->in_stream; state->out_stream.last_timestamp = timestamp; - state->out_stream.ssrc = rtp_hdr->ssrc - 1; /* SSRC change <=> initialisation */ + state->out_stream.ssrc = ssrc - 1; /* SSRC change <=> initialisation */ LOGP(DMGCP, LOGL_INFO, "Initializing stream on 0x%x SSRC: %u timestamp: %u " + "pkt-duration: %d, from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + state->seq_offset, state->packet_duration, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } else if (state->in_stream.ssrc != ssrc) { + int32_t tsdelta; + + LOGP(DMGCP, LOGL_NOTICE, + "The SSRC changed on 0x%x SSRC: %u " "from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); - } else if (state->in_stream.ssrc != rtp_hdr->ssrc) { - int32_t tsdelta = state->out_stream.last_tsdelta; + + tsdelta = state->out_stream.last_tsdelta; if (tsdelta == 0) { - tsdelta = rtp_end->rate * rtp_end->frames_per_packet * - rtp_end->frame_duration_num / - rtp_end->frame_duration_den; + tsdelta = state->packet_duration; LOGP(DMGCP, LOGL_NOTICE, "Computed timestamp delta %d based on " "rate %d, num frames %d, frame duration %d/%d\n", @@ -259,18 +271,25 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta rtp_end->frame_duration_num, rtp_end->frame_duration_den); } - state->in_stream.ssrc = rtp_hdr->ssrc; - state->seq_offset = (state->out_stream.last_seq + 1) - seq; - state->timestamp_offset = - (state->out_stream.last_timestamp + tsdelta) - timestamp; - state->patch = rtp_end->force_constant_ssrc; - LOGP(DMGCP, LOGL_NOTICE, - "The SSRC changed on 0x%x SSRC: %u offset: %d tsdelta: %d " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, tsdelta, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); + state->in_stream.ssrc = ssrc; + if (rtp_end->force_constant_ssrc) { + state->seq_offset = + (state->out_stream.last_seq + 1) - seq; + state->timestamp_offset = + (state->out_stream.last_timestamp + tsdelta) - + timestamp; + state->patch = 1; + ssrc = state->orig_ssrc; + + LOGP(DMGCP, LOGL_NOTICE, + "SSRC patching enabled on 0x%x SSRC: %u " + "offset: %d tsdelta: %d " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + state->seq_offset, tsdelta, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } state->in_stream.last_tsdelta = 0; } else { @@ -278,6 +297,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta check_rtp_timestamp(endp, &state->in_stream, rtp_end, addr, seq, timestamp, "input", &state->in_stream.last_tsdelta); + + if (state->patch) + ssrc = state->orig_ssrc; } /* Save before patching */ @@ -288,14 +310,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta if (state->patch) { seq += state->seq_offset; rtp_hdr->sequence = htons(seq); - rtp_hdr->ssrc = state->orig_ssrc; + rtp_hdr->ssrc = htonl(ssrc); timestamp += state->timestamp_offset; rtp_hdr->timestamp = htonl(timestamp); - } + } else + ssrc = ntohl(rtp_hdr->ssrc); /* Check again, whether the timestamps are still valid */ - if (state->out_stream.ssrc == rtp_hdr->ssrc) + if (state->out_stream.ssrc == ssrc) check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr, seq, timestamp, "output", &state->out_stream.last_tsdelta); @@ -332,7 +355,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta /* Save output values */ state->out_stream.last_seq = seq; state->out_stream.last_timestamp = timestamp; - state->out_stream.ssrc = rtp_hdr->ssrc; + state->out_stream.ssrc = ssrc; if (payload < 0) return; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 6d31ace..8693396 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -834,6 +834,9 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) } } + mgcp_rtp_end_config(endp, 1, &endp->net_end); + mgcp_rtp_end_config(endp, 1, &endp->bts_end); + /* modify */ LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n", ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port)); -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 3 16:17:08 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 3 Dec 2013 17:17:08 +0100 Subject: [PATCH 5/6] mgcp/rtp: Only patch SSRC once after MDCX if enabled In-Reply-To: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386087429-11618-5-git-send-email-jerlbeck@sysmocom.de> Currently the output SSRC is always forced to be the same if SSRC patching is enabled. This patch modifies this to optionally restrict the number of SSRC changes that will be corrected. Note that the configuration only allows for the 'once' mode and 'off'. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 2 +- openbsc/src/libmgcp/mgcp_network.c | 2 ++ openbsc/src/libmgcp/mgcp_protocol.c | 4 +++- openbsc/src/libmgcp/mgcp_vty.c | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index b16bd49..255161f 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -85,7 +85,7 @@ struct mgcp_rtp_end { char *fmtp_extra; /* RTP patching */ - int force_constant_ssrc; + int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ int force_constant_timing; /* diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 137ddba..b9bfc5e 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -280,6 +280,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta timestamp; state->patch = 1; ssrc = state->orig_ssrc; + if (rtp_end->force_constant_ssrc != -1) + rtp_end->force_constant_ssrc -= 1; LOGP(DMGCP, LOGL_NOTICE, "SSRC patching enabled on 0x%x SSRC: %u " diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 8693396..f29e927 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -617,8 +617,10 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, { struct mgcp_trunk_config *tcfg = endp->tcfg; + int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc; + rtp->force_constant_timing = tcfg->force_constant_timing; - rtp->force_constant_ssrc = tcfg->force_constant_ssrc; + rtp->force_constant_ssrc = patch_ssrc ? 1 : 0; } static struct msgb *handle_create_con(struct mgcp_parse_data *p) diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 16b973f..38570cb 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -613,7 +613,7 @@ DEFUN(cfg_trunk_patch_rtp_ssrc, RTP_PATCH_STR "Force a fixed SSRC\n" "Disable\n" - "Enable\n" + "Enable (once per MDCX)\n" ) { struct mgcp_trunk_config *trunk = vty->index; -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 3 16:17:09 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 3 Dec 2013 17:17:09 +0100 Subject: [PATCH 6/6] mgcp: Fix RTP timestamps if enabled In-Reply-To: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386087429-11618-6-git-send-email-jerlbeck@sysmocom.de> This forces the output timing to fulfill dTS = dSegNo * fixedPacketDuration where dSegNo = seqNo - lastSeqNo. If timestamp patching is enabled, the output timestamp will be set to lastTimestamp + dTS. This kind of relative updating is used to handle seqNo- and timestamp-wraparounds properly. Ticket: OW#1065 Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 21 ++++++++++++++++ openbsc/tests/mgcp/mgcp_test.ok | 48 ++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index b9bfc5e..a68c43c 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -308,6 +308,27 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->in_stream.last_timestamp = timestamp; state->in_stream.last_seq = seq; + if (rtp_end->force_constant_timing && + state->out_stream.ssrc == ssrc && state->packet_duration) { + int delta_seq = seq + state->seq_offset - state->out_stream.last_seq; + int timestamp_offset = + state->out_stream.last_timestamp - timestamp + + delta_seq * state->packet_duration; + if (state->timestamp_offset != timestamp_offset) { + state->timestamp_offset = timestamp_offset; + state->patch = 1; + + LOGP(DMGCP, LOGL_NOTICE, + "Timestamp patching enabled on 0x%x SSRC: %u " + "SeqNo delta: %d, TS offset: %d, " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + delta_seq, state->timestamp_offset, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } + } + /* apply the offset and store it back to the packet */ if (state->patch) { seq += state->seq_offset; diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 71293ad..c018fb2 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -78,32 +78,32 @@ Testing packet error detection, patch timestamps. TS: 0, dTS: 0, TS Errs: in 0, out 0 TS: 160, dTS: 160, TS Errs: in 0, out 0 TS: 320, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 1, out 1 -TS: 480, dTS: 160, TS Errs: in 1, out 1 -TS: 640, dTS: 160, TS Errs: in 1, out 1 -TS: 960, dTS: 320, TS Errs: in 2, out 2 -TS: 1120, dTS: 160, TS Errs: in 3, out 3 -TS: 1280, dTS: 160, TS Errs: in 3, out 3 -TS: 1400, dTS: 120, TS Errs: in 4, out 4 -TS: 1560, dTS: 160, TS Errs: in 5, out 5 -TS: 1720, dTS: 160, TS Errs: in 5, out 5 -TS: 34688, dTS: 160, TS Errs: in 5, out 5 -TS: 34848, dTS: 160, TS Errs: in 5, out 5 -TS: 35008, dTS: 160, TS Errs: in 5, out 5 +TS: 480, dTS: 160, TS Errs: in 1, out 0 +TS: 640, dTS: 160, TS Errs: in 1, out 0 +TS: 800, dTS: 160, TS Errs: in 1, out 0 +TS: 960, dTS: 160, TS Errs: in 2, out 0 +TS: 1120, dTS: 160, TS Errs: in 3, out 0 +TS: 1280, dTS: 160, TS Errs: in 3, out 0 +TS: 1440, dTS: 160, TS Errs: in 4, out 0 +TS: 1600, dTS: 160, TS Errs: in 5, out 0 +TS: 1760, dTS: 160, TS Errs: in 5, out 0 +TS: 34728, dTS: 160, TS Errs: in 5, out 0 +TS: 34888, dTS: 160, TS Errs: in 5, out 0 +TS: 35048, dTS: 160, TS Errs: in 5, out 0 Testing packet error detection, patch SSRC, patch timestamps. TS: 0, dTS: 0, TS Errs: in 0, out 0 TS: 160, dTS: 160, TS Errs: in 0, out 0 TS: 320, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 1, out 1 -TS: 480, dTS: 160, TS Errs: in 1, out 1 -TS: 640, dTS: 160, TS Errs: in 1, out 1 -TS: 960, dTS: 320, TS Errs: in 2, out 2 -TS: 1120, dTS: 160, TS Errs: in 3, out 3 -TS: 1280, dTS: 160, TS Errs: in 3, out 3 -TS: 1400, dTS: 120, TS Errs: in 4, out 4 -TS: 1560, dTS: 160, TS Errs: in 5, out 5 -TS: 1720, dTS: 160, TS Errs: in 5, out 5 -TS: 1880, dTS: 160, TS Errs: in 5, out 5 -TS: 2040, dTS: 160, TS Errs: in 5, out 5 -TS: 2200, dTS: 160, TS Errs: in 5, out 5 +TS: 480, dTS: 160, TS Errs: in 1, out 0 +TS: 640, dTS: 160, TS Errs: in 1, out 0 +TS: 800, dTS: 160, TS Errs: in 1, out 0 +TS: 960, dTS: 160, TS Errs: in 2, out 0 +TS: 1120, dTS: 160, TS Errs: in 3, out 0 +TS: 1280, dTS: 160, TS Errs: in 3, out 0 +TS: 1440, dTS: 160, TS Errs: in 4, out 0 +TS: 1600, dTS: 160, TS Errs: in 5, out 0 +TS: 1760, dTS: 160, TS Errs: in 5, out 0 +TS: 1920, dTS: 160, TS Errs: in 5, out 0 +TS: 2080, dTS: 160, TS Errs: in 5, out 0 +TS: 2240, dTS: 160, TS Errs: in 5, out 0 Done -- 1.7.9.5 From jerlbeck at sysmocom.de Thu Dec 5 16:44:16 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:16 +0100 Subject: [PATCH 01/10] mgcp/rtp: Add more test cases for RTP header patching In-Reply-To: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> This patch extends the existing RTP error check test by adding a check for timestamp errors after SSRC changes and a check for a segno delta of 2 (with a timestamp delta of 320). To test SSRC patching too, a corresponding line will be written on each SSRC change that has been detected in the output stream. In addition there is now support for selectively enabling/disabling SSRC and timestamp patching. The RTP test sequence is repeated for all combinations thereof. Sponsored-by: On-Waves ehf --- openbsc/tests/mgcp/mgcp_test.c | 40 ++++++++++++++++++-- openbsc/tests/mgcp/mgcp_test.ok | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index c58f52d..8e130bb 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -510,13 +510,33 @@ struct rtp_packet_info test_rtp_packets1[] = { /* RTP: SeqNo=14, TS=35008 */ {0.280000, 20, "\x80\x62\x00\x0E\x00\x00\x88\xC0\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* Non 20ms RTP timestamp (delta = 120): */ + /* RTP: SeqNo=15, TS=35128 */ + {0.300000, 20, "\x80\x62\x00\x0F\x00\x00\x89\x38\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* RTP: SeqNo=16, TS=35288 */ + {0.320000, 20, "\x80\x62\x00\x10\x00\x00\x89\xD8\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* RTP: SeqNo=17, TS=35448 */ + {0.340000, 20, "\x80\x62\x00\x11\x00\x00\x8A\x78\x10\x20\x30\x40" + "\x01\x23\x45\x67\x8A\xAB\xCD\xEF"}, + /* SeqNo increment by 2, RTP timestamp delta = 320: */ + /* RTP: SeqNo=19, TS=35768 */ + {0.360000, 20, "\x80\x62\x00\x13\x00\x00\x8B\xB8\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* RTP: SeqNo=20, TS=35928 */ + {0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\x58\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* RTP: SeqNo=21, TS=36088 */ + {0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\xF8\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, }; void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, char *data, int len); -static void test_packet_error_detection(void) +static void test_packet_error_detection(int patch_ssrc, int patch_ts) { int i; @@ -526,8 +546,11 @@ static void test_packet_error_detection(void) struct mgcp_rtp_end *rtp = &endp.net_end; struct sockaddr_in addr = {0}; char buffer[4096]; + uint32_t last_ssrc = 0; - printf("Testing packet error detection.\n"); + printf("Testing packet error detection%s%s.\n", + patch_ssrc ? ", patch SSRC" : "", + patch_ts ? ", patch timestamps" : ""); memset(&trunk, 0, sizeof(trunk)); memset(&endp, 0, sizeof(endp)); @@ -545,7 +568,7 @@ static void test_packet_error_detection(void) mgcp_free_endp(&endp); rtp->payload_type = 98; - endp.allow_patch = 1; + endp.allow_patch = patch_ssrc; for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { struct rtp_packet_info *info = test_rtp_packets1 + i; @@ -557,6 +580,12 @@ static void test_packet_error_detection(void) mgcp_patch_and_count(&endp, &state, rtp, &addr, buffer, info->len); + if (state.out_stream.ssrc != last_ssrc) { + printf("Output SSRC changed to %08x\n", + ntohl(state.out_stream.ssrc)); + last_ssrc = state.out_stream.ssrc; + } + printf("TS: %d, dTS: %d, TS Errs: in %d, out %d\n", state.out_stream.last_timestamp, state.out_stream.last_tsdelta, @@ -575,7 +604,10 @@ int main(int argc, char **argv) test_packet_loss_calc(); test_rqnt_cb(); test_mgcp_stats(); - test_packet_error_detection(); + test_packet_error_detection(1, 0); + test_packet_error_detection(0, 0); + test_packet_error_detection(0, 1); + test_packet_error_detection(1, 1); printf("Done\n"); return EXIT_SUCCESS; diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 3bfd78b..57d3bfb 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -42,7 +42,79 @@ Testing packet loss calculation. Testing stat parsing Parsing result: 0 Parsing result: 0 +Testing packet error detection, patch SSRC. +Output SSRC changed to 11223344 +TS: 0, dTS: 0, TS Errs: in 0, out 0 +TS: 160, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 1, out 1 +TS: 480, dTS: 160, TS Errs: in 1, out 1 +TS: 640, dTS: 160, TS Errs: in 1, out 1 +TS: 960, dTS: 320, TS Errs: in 2, out 2 +TS: 1120, dTS: 160, TS Errs: in 3, out 3 +TS: 1280, dTS: 160, TS Errs: in 3, out 3 +TS: 1400, dTS: 120, TS Errs: in 4, out 4 +TS: 1560, dTS: 160, TS Errs: in 5, out 5 +TS: 1720, dTS: 160, TS Errs: in 5, out 5 +TS: 1880, dTS: 160, TS Errs: in 5, out 5 +TS: 2040, dTS: 160, TS Errs: in 5, out 5 +TS: 2200, dTS: 160, TS Errs: in 5, out 5 +TS: 2320, dTS: 120, TS Errs: in 6, out 6 +TS: 2480, dTS: 160, TS Errs: in 7, out 7 +TS: 2640, dTS: 160, TS Errs: in 7, out 7 +TS: 2960, dTS: 160, TS Errs: in 7, out 7 +TS: 3120, dTS: 160, TS Errs: in 7, out 7 +TS: 3280, dTS: 160, TS Errs: in 8, out 8 Testing packet error detection. +Output SSRC changed to 11223344 +TS: 0, dTS: 0, TS Errs: in 0, out 0 +TS: 160, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 1, out 1 +TS: 480, dTS: 160, TS Errs: in 1, out 1 +TS: 640, dTS: 160, TS Errs: in 1, out 1 +TS: 960, dTS: 320, TS Errs: in 2, out 2 +TS: 1120, dTS: 160, TS Errs: in 3, out 3 +TS: 1280, dTS: 160, TS Errs: in 3, out 3 +TS: 1400, dTS: 120, TS Errs: in 4, out 4 +TS: 1560, dTS: 160, TS Errs: in 5, out 5 +TS: 1720, dTS: 160, TS Errs: in 5, out 5 +Output SSRC changed to 10203040 +TS: 34688, dTS: 32968, TS Errs: in 5, out 6 +TS: 34848, dTS: 160, TS Errs: in 5, out 7 +TS: 35008, dTS: 160, TS Errs: in 5, out 7 +TS: 35128, dTS: 120, TS Errs: in 6, out 8 +TS: 35288, dTS: 160, TS Errs: in 7, out 9 +TS: 35448, dTS: 160, TS Errs: in 7, out 9 +TS: 35768, dTS: 160, TS Errs: in 7, out 9 +TS: 35928, dTS: 160, TS Errs: in 7, out 9 +TS: 36088, dTS: 160, TS Errs: in 8, out 10 +Testing packet error detection, patch timestamps. +Output SSRC changed to 11223344 +TS: 0, dTS: 0, TS Errs: in 0, out 0 +TS: 160, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 0, out 0 +TS: 320, dTS: 160, TS Errs: in 1, out 1 +TS: 480, dTS: 160, TS Errs: in 1, out 1 +TS: 640, dTS: 160, TS Errs: in 1, out 1 +TS: 960, dTS: 320, TS Errs: in 2, out 2 +TS: 1120, dTS: 160, TS Errs: in 3, out 3 +TS: 1280, dTS: 160, TS Errs: in 3, out 3 +TS: 1400, dTS: 120, TS Errs: in 4, out 4 +TS: 1560, dTS: 160, TS Errs: in 5, out 5 +TS: 1720, dTS: 160, TS Errs: in 5, out 5 +Output SSRC changed to 10203040 +TS: 34688, dTS: 32968, TS Errs: in 5, out 6 +TS: 34848, dTS: 160, TS Errs: in 5, out 7 +TS: 35008, dTS: 160, TS Errs: in 5, out 7 +TS: 35128, dTS: 120, TS Errs: in 6, out 8 +TS: 35288, dTS: 160, TS Errs: in 7, out 9 +TS: 35448, dTS: 160, TS Errs: in 7, out 9 +TS: 35768, dTS: 160, TS Errs: in 7, out 9 +TS: 35928, dTS: 160, TS Errs: in 7, out 9 +TS: 36088, dTS: 160, TS Errs: in 8, out 10 +Testing packet error detection, patch SSRC, patch timestamps. +Output SSRC changed to 11223344 TS: 0, dTS: 0, TS Errs: in 0, out 0 TS: 160, dTS: 160, TS Errs: in 0, out 0 TS: 320, dTS: 160, TS Errs: in 0, out 0 @@ -58,4 +130,10 @@ TS: 1720, dTS: 160, TS Errs: in 5, out 5 TS: 1880, dTS: 160, TS Errs: in 5, out 5 TS: 2040, dTS: 160, TS Errs: in 5, out 5 TS: 2200, dTS: 160, TS Errs: in 5, out 5 +TS: 2320, dTS: 120, TS Errs: in 6, out 6 +TS: 2480, dTS: 160, TS Errs: in 7, out 7 +TS: 2640, dTS: 160, TS Errs: in 7, out 7 +TS: 2960, dTS: 160, TS Errs: in 7, out 7 +TS: 3120, dTS: 160, TS Errs: in 7, out 7 +TS: 3280, dTS: 160, TS Errs: in 8, out 8 Done -- 1.7.9.5 From jerlbeck at sysmocom.de Thu Dec 5 16:44:17 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:17 +0100 Subject: [PATCH 02/10] mgcp/rtp: Fix output timing error counter In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-2-git-send-email-jerlbeck@sysmocom.de> The tsdelta computation and error detection didn't handle the intialisation phase properly. This patches fixes this by skipping the output timing validation when the SSRCs don't match. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 17 ++++++++++++++--- openbsc/tests/mgcp/mgcp_test.ok | 36 ++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 75d39c1..53f1a20 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -237,6 +237,16 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->jitter = 0; state->transit = arrival_time - timestamp; state->out_stream = state->in_stream; + state->out_stream.last_timestamp = timestamp; + /* force output SSRC change */ + state->out_stream.ssrc = rtp_hdr->ssrc - 1; + LOGP(DMGCP, LOGL_INFO, + "Initializing stream on 0x%x SSRC: %u timestamp: %u " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + state->seq_offset, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); } else if (state->in_stream.ssrc != rtp_hdr->ssrc) { int32_t tsdelta = state->out_stream.last_tsdelta; if (tsdelta == 0) { @@ -286,9 +296,10 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta } /* Check again, whether the timestamps are still valid */ - check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr, - seq, timestamp, "output", - &state->out_stream.last_tsdelta); + if (state->out_stream.ssrc == rtp_hdr->ssrc) + check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr, + seq, timestamp, "output", + &state->out_stream.last_tsdelta); /* * The below takes the shape of the validation from Appendix A. Check diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 57d3bfb..4d3caac 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -80,15 +80,15 @@ TS: 1400, dTS: 120, TS Errs: in 4, out 4 TS: 1560, dTS: 160, TS Errs: in 5, out 5 TS: 1720, dTS: 160, TS Errs: in 5, out 5 Output SSRC changed to 10203040 -TS: 34688, dTS: 32968, TS Errs: in 5, out 6 -TS: 34848, dTS: 160, TS Errs: in 5, out 7 -TS: 35008, dTS: 160, TS Errs: in 5, out 7 -TS: 35128, dTS: 120, TS Errs: in 6, out 8 -TS: 35288, dTS: 160, TS Errs: in 7, out 9 -TS: 35448, dTS: 160, TS Errs: in 7, out 9 -TS: 35768, dTS: 160, TS Errs: in 7, out 9 -TS: 35928, dTS: 160, TS Errs: in 7, out 9 -TS: 36088, dTS: 160, TS Errs: in 8, out 10 +TS: 34688, dTS: 160, TS Errs: in 5, out 5 +TS: 34848, dTS: 160, TS Errs: in 5, out 5 +TS: 35008, dTS: 160, TS Errs: in 5, out 5 +TS: 35128, dTS: 120, TS Errs: in 6, out 6 +TS: 35288, dTS: 160, TS Errs: in 7, out 7 +TS: 35448, dTS: 160, TS Errs: in 7, out 7 +TS: 35768, dTS: 160, TS Errs: in 7, out 7 +TS: 35928, dTS: 160, TS Errs: in 7, out 7 +TS: 36088, dTS: 160, TS Errs: in 8, out 8 Testing packet error detection, patch timestamps. Output SSRC changed to 11223344 TS: 0, dTS: 0, TS Errs: in 0, out 0 @@ -104,15 +104,15 @@ TS: 1400, dTS: 120, TS Errs: in 4, out 4 TS: 1560, dTS: 160, TS Errs: in 5, out 5 TS: 1720, dTS: 160, TS Errs: in 5, out 5 Output SSRC changed to 10203040 -TS: 34688, dTS: 32968, TS Errs: in 5, out 6 -TS: 34848, dTS: 160, TS Errs: in 5, out 7 -TS: 35008, dTS: 160, TS Errs: in 5, out 7 -TS: 35128, dTS: 120, TS Errs: in 6, out 8 -TS: 35288, dTS: 160, TS Errs: in 7, out 9 -TS: 35448, dTS: 160, TS Errs: in 7, out 9 -TS: 35768, dTS: 160, TS Errs: in 7, out 9 -TS: 35928, dTS: 160, TS Errs: in 7, out 9 -TS: 36088, dTS: 160, TS Errs: in 8, out 10 +TS: 34688, dTS: 160, TS Errs: in 5, out 5 +TS: 34848, dTS: 160, TS Errs: in 5, out 5 +TS: 35008, dTS: 160, TS Errs: in 5, out 5 +TS: 35128, dTS: 120, TS Errs: in 6, out 6 +TS: 35288, dTS: 160, TS Errs: in 7, out 7 +TS: 35448, dTS: 160, TS Errs: in 7, out 7 +TS: 35768, dTS: 160, TS Errs: in 7, out 7 +TS: 35928, dTS: 160, TS Errs: in 7, out 7 +TS: 36088, dTS: 160, TS Errs: in 8, out 8 Testing packet error detection, patch SSRC, patch timestamps. Output SSRC changed to 11223344 TS: 0, dTS: 0, TS Errs: in 0, out 0 -- 1.7.9.5 From jerlbeck at sysmocom.de Thu Dec 5 16:44:18 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:18 +0100 Subject: [PATCH 03/10] mgcp/rtp: Add RTP header patch mode configuration In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-3-git-send-email-jerlbeck@sysmocom.de> This adds datastructures and a VTY frontend to configure the different type of RTP header patching: SSRC and timestamp. Note that timestamp patching is not yet implemented. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp.h | 4 +++ openbsc/include/openbsc/mgcp_internal.h | 10 ++++-- openbsc/src/libmgcp/mgcp_network.c | 2 +- openbsc/src/libmgcp/mgcp_protocol.c | 15 ++++++++- openbsc/src/libmgcp/mgcp_vty.c | 56 ++++++++++++++++++++++++++++++- openbsc/tests/mgcp/mgcp_test.c | 6 +++- 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 4e22e0f..8ab52ce 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -118,6 +118,10 @@ struct mgcp_trunk_config { int omit_rtcp; + /* RTP patching */ + int force_constant_ssrc; /* 0: don't, 1: once */ + int force_constant_timing; + /* spec handling */ int force_realloc; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 0b52c1c..02e193d 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -83,6 +83,10 @@ struct mgcp_rtp_end { int frames_per_packet; char *fmtp_extra; + /* RTP patching */ + int force_constant_ssrc; + int force_constant_timing; + /* * Each end has a socket... */ @@ -142,9 +146,6 @@ struct mgcp_endpoint { struct mgcp_rtp_state net_state; struct mgcp_rtp_state bts_state; - /* SSRC/seq/ts patching for loop */ - int allow_patch; - /* fields for re-transmission */ char *last_trans; char *last_response; @@ -176,6 +177,9 @@ static inline int endp_back_channel(int endpoint) struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); +void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, + struct mgcp_rtp_end *rtp); + void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, uint32_t *expected, int *loss); uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *); diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 53f1a20..367cebd 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -264,7 +264,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->seq_offset = (state->out_stream.last_seq + 1) - seq; state->timestamp_offset = (state->out_stream.last_timestamp + tsdelta) - timestamp; - state->patch = endp->allow_patch; + state->patch = rtp_end->force_constant_ssrc; LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed on 0x%x SSRC: %u offset: %d tsdelta: %d " "from %s:%d in %d\n", diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index a0b905d..d7e4745 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -614,6 +614,15 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) return found_media; } +void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, + struct mgcp_rtp_end *rtp) +{ + struct mgcp_trunk_config *tcfg = endp->tcfg; + + rtp->force_constant_timing = tcfg->force_constant_timing; + rtp->force_constant_ssrc = tcfg->force_constant_ssrc; +} + static struct msgb *handle_create_con(struct mgcp_parse_data *p) { struct mgcp_trunk_config *tcfg; @@ -688,6 +697,8 @@ mgcp_header_done: /* initialize */ endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0; + mgcp_rtp_end_config(endp, 0, &endp->net_end); + mgcp_rtp_end_config(endp, 0, &endp->bts_end); /* set to zero until we get the info */ memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr)); @@ -825,6 +836,9 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) } } + mgcp_rtp_end_config(endp, 1, &endp->net_end); + mgcp_rtp_end_config(endp, 1, &endp->bts_end); + /* modify */ LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n", ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port)); @@ -1121,7 +1135,6 @@ void mgcp_free_endp(struct mgcp_endpoint *endp) memset(&endp->bts_state, 0, sizeof(endp->bts_state)); endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE; - endp->allow_patch = 0; memset(&endp->taps, 0, sizeof(endp->taps)); } diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 5aeb393..16b973f 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -31,6 +31,7 @@ #include #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n" +#define RTP_PATCH_STR "Modify RTP packet header in both directions\n" static struct mgcp_config *g_cfg = NULL; @@ -511,6 +512,13 @@ static int config_write_trunk(struct vty *vty) vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); else vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); + if (trunk->force_constant_ssrc || trunk->force_constant_timing) { + vty_out(vty, " rtp-patch ssrc %d%s", + trunk->force_constant_ssrc, VTY_NEWLINE); + vty_out(vty, " rtp-patch timestamp %d%s", + trunk->force_constant_timing, VTY_NEWLINE); + } else + vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); if (trunk->audio_fmtp_extra) vty_out(vty, " sdp audio fmtp-extra %s%s", trunk->audio_fmtp_extra, VTY_NEWLINE); @@ -599,6 +607,46 @@ DEFUN(cfg_trunk_no_omit_rtcp, return CMD_SUCCESS; } +DEFUN(cfg_trunk_patch_rtp_ssrc, + cfg_trunk_patch_rtp_ssrc_cmd, + "rtp-patch ssrc (0|1)", + RTP_PATCH_STR + "Force a fixed SSRC\n" + "Disable\n" + "Enable\n" + ) +{ + struct mgcp_trunk_config *trunk = vty->index; + trunk->force_constant_ssrc = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_patch_rtp_ts, + cfg_trunk_patch_rtp_ts_cmd, + "rtp-patch timestamp (0|1)", + RTP_PATCH_STR + "Adjust RTP timestamp\n" + "Disable\n" + "Enable\n" + ) +{ + struct mgcp_trunk_config *trunk = vty->index; + trunk->force_constant_timing = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_no_patch_rtp, + cfg_trunk_no_patch_rtp_cmd, + "no rtp-patch", + NO_STR RTP_PATCH_STR) +{ + struct mgcp_trunk_config *trunk = vty->index; + trunk->force_constant_ssrc = 0; + trunk->force_constant_timing = 0; + return CMD_SUCCESS; +} + + DEFUN(loop_endp, loop_endp_cmd, "loop-endpoint <0-64> NAME (0|1)", @@ -636,7 +684,10 @@ DEFUN(loop_endp, endp->conn_mode = MGCP_CONN_LOOPBACK; else endp->conn_mode = endp->orig_mode; - endp->allow_patch = 1; + + /* Handle it like a MDCX, switch on SSRC patching if enabled */ + mgcp_rtp_end_config(endp, 1, &endp->bts_end); + mgcp_rtp_end_config(endp, 1, &endp->net_end); return CMD_SUCCESS; } @@ -839,6 +890,9 @@ int mgcp_vty_init(void) install_element(TRUNK_NODE, &cfg_trunk_loop_cmd); install_element(TRUNK_NODE, &cfg_trunk_omit_rtcp_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_omit_rtcp_cmd); + install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd); + install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd); + install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd); install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd); return 0; diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 8e130bb..908773a 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -558,6 +558,9 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) trunk.number_endpoints = 1; trunk.endpoints = &endp; + trunk.force_constant_ssrc = patch_ssrc; + trunk.force_constant_timing = patch_ts; + endp.tcfg = &trunk; /* This doesn't free endp but resets/frees all fields of the structure @@ -568,7 +571,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) mgcp_free_endp(&endp); rtp->payload_type = 98; - endp.allow_patch = patch_ssrc; for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { struct rtp_packet_info *info = test_rtp_packets1 + i; @@ -577,6 +579,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) OSMO_ASSERT(info->len >= 0); memmove(buffer, info->data, info->len); + mgcp_rtp_end_config(&endp, 1, rtp); + mgcp_patch_and_count(&endp, &state, rtp, &addr, buffer, info->len); -- 1.7.9.5 From holger at freyther.de Mon Dec 9 15:57:03 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Mon, 9 Dec 2013 16:57:03 +0100 Subject: [PATCH 03/10] mgcp/rtp: Add RTP header patch mode configuration In-Reply-To: <1386261865-23713-3-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-3-git-send-email-jerlbeck@sysmocom.de> Message-ID: <20131209155703.GD22686@xiaoyu.lan> On Thu, Dec 05, 2013 at 05:44:18PM +0100, Jacob Erlbeck wrote: > +void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, > + struct mgcp_rtp_end *rtp); config sounds more like an object than an action? The patch looks fine and I intend to apply it today. From jerlbeck at sysmocom.de Thu Dec 5 16:44:19 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:19 +0100 Subject: [PATCH 04/10] mgcp/rtp: Change the log message shown when the SSRC changes In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-4-git-send-email-jerlbeck@sysmocom.de> Show old and new SSRC. Move logging command upward to show the values immediately after the change has been detected and before any fixing attempt is made. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 367cebd..d19b56e 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -249,6 +249,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta endp->conn_mode); } else if (state->in_stream.ssrc != rtp_hdr->ssrc) { int32_t tsdelta = state->out_stream.last_tsdelta; + + LOGP(DMGCP, LOGL_NOTICE, + "The SSRC changed on 0x%x: %u -> %u " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), + state->in_stream.ssrc, rtp_hdr->ssrc, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + if (tsdelta == 0) { tsdelta = rtp_end->rate * rtp_end->frames_per_packet * rtp_end->frame_duration_num / @@ -265,13 +274,6 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->timestamp_offset = (state->out_stream.last_timestamp + tsdelta) - timestamp; state->patch = rtp_end->force_constant_ssrc; - LOGP(DMGCP, LOGL_NOTICE, - "The SSRC changed on 0x%x SSRC: %u offset: %d tsdelta: %d " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, tsdelta, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); state->in_stream.last_tsdelta = 0; } else { -- 1.7.9.5 From jerlbeck at sysmocom.de Thu Dec 5 16:44:20 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:20 +0100 Subject: [PATCH 05/10] mgcp/rtp: Only update RTP header field offsets if enabled In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-5-git-send-email-jerlbeck@sysmocom.de> Currently seq_offset and timestamp_offset are updated on each SSRC change even when SSRC patching is not allowed. This patch fixes this by changing mgcp_patch_and_count() to only update these fields when SSRC patching is allowed. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 44 +++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index d19b56e..c14b913 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -248,8 +248,6 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } else if (state->in_stream.ssrc != rtp_hdr->ssrc) { - int32_t tsdelta = state->out_stream.last_tsdelta; - LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed on 0x%x: %u -> %u " "from %s:%d in %d\n", @@ -258,22 +256,36 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); - if (tsdelta == 0) { - tsdelta = rtp_end->rate * rtp_end->frames_per_packet * - rtp_end->frame_duration_num / - rtp_end->frame_duration_den; + state->in_stream.ssrc = rtp_hdr->ssrc; + if (rtp_end->force_constant_ssrc) { + int32_t tsdelta = state->out_stream.last_tsdelta; + if (tsdelta == 0) { + tsdelta = rtp_end->rate * rtp_end->frames_per_packet * + rtp_end->frame_duration_num / + rtp_end->frame_duration_den; + LOGP(DMGCP, LOGL_NOTICE, + "Computed timestamp delta %d based on " + "rate %d, num frames %d, frame duration %d/%d\n", + tsdelta, rtp_end->rate, rtp_end->frames_per_packet, + rtp_end->frame_duration_num, + rtp_end->frame_duration_den); + } + state->seq_offset = + (state->out_stream.last_seq + 1) - seq; + state->timestamp_offset = + (state->out_stream.last_timestamp + tsdelta) - + timestamp; + state->patch = 1; + LOGP(DMGCP, LOGL_NOTICE, - "Computed timestamp delta %d based on " - "rate %d, num frames %d, frame duration %d/%d\n", - tsdelta, rtp_end->rate, rtp_end->frames_per_packet, - rtp_end->frame_duration_num, - rtp_end->frame_duration_den); + "SSRC patching enabled on 0x%x SSRC: %u " + "offset: %d tsdelta: %d " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + state->seq_offset, tsdelta, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); } - state->in_stream.ssrc = rtp_hdr->ssrc; - state->seq_offset = (state->out_stream.last_seq + 1) - seq; - state->timestamp_offset = - (state->out_stream.last_timestamp + tsdelta) - timestamp; - state->patch = rtp_end->force_constant_ssrc; state->in_stream.last_tsdelta = 0; } else { -- 1.7.9.5 From jerlbeck at sysmocom.de Thu Dec 5 16:44:21 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:21 +0100 Subject: [PATCH 06/10] mgcp/rtp: Compute default packet duration at state initialisiation In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-6-git-send-email-jerlbeck@sysmocom.de> This patch adds a packet_duration field to mgcp_rtp_state which contains the RTP packet's duration in RTP timestamp units or 0, when the duration is unknown or not fixed. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 1 + openbsc/src/libmgcp/mgcp_network.c | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 02e193d..b16bd49 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -59,6 +59,7 @@ struct mgcp_rtp_state { int cycles; int32_t timestamp_offset; + uint32_t packet_duration; uint32_t jitter; int32_t transit; diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index c14b913..f177339 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -236,15 +236,19 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->initialized = 1; state->jitter = 0; state->transit = arrival_time - timestamp; + state->packet_duration = + rtp_end->rate * rtp_end->frames_per_packet * + rtp_end->frame_duration_num / + rtp_end->frame_duration_den; state->out_stream = state->in_stream; state->out_stream.last_timestamp = timestamp; /* force output SSRC change */ state->out_stream.ssrc = rtp_hdr->ssrc - 1; LOGP(DMGCP, LOGL_INFO, "Initializing stream on 0x%x SSRC: %u timestamp: %u " - "from %s:%d in %d\n", + "pkt-duration: %d, from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, + state->seq_offset, state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } else if (state->in_stream.ssrc != rtp_hdr->ssrc) { @@ -260,15 +264,14 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta if (rtp_end->force_constant_ssrc) { int32_t tsdelta = state->out_stream.last_tsdelta; if (tsdelta == 0) { - tsdelta = rtp_end->rate * rtp_end->frames_per_packet * - rtp_end->frame_duration_num / - rtp_end->frame_duration_den; + tsdelta = state->packet_duration; LOGP(DMGCP, LOGL_NOTICE, - "Computed timestamp delta %d based on " - "rate %d, num frames %d, frame duration %d/%d\n", - tsdelta, rtp_end->rate, rtp_end->frames_per_packet, - rtp_end->frame_duration_num, - rtp_end->frame_duration_den); + "Timestamp delta is not available on 0x%x, " + "using packet duration instead: %d " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), tsdelta, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); } state->seq_offset = (state->out_stream.last_seq + 1) - seq; -- 1.7.9.5 From jerlbeck at sysmocom.de Thu Dec 5 16:44:22 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:22 +0100 Subject: [PATCH 07/10] mgcp/rtp: Use SSRC in proper byte ordering In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-7-git-send-email-jerlbeck@sysmocom.de> The ssrc has been used without respect to proper byte ordering in mgcp_patch_and_count(). This only affected log messages. This patch introduces a new variable 'ssrc' that takes the value of the SSRC in proper byte order. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 22 +++++++++++++--------- openbsc/tests/mgcp/mgcp_test.c | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index f177339..abb48f5 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -216,7 +216,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta uint32_t arrival_time; int32_t transit, d; uint16_t seq, udelta; - uint32_t timestamp; + uint32_t timestamp, ssrc; struct rtp_hdr *rtp_hdr; int payload = rtp_end->payload_type; @@ -227,10 +227,11 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta seq = ntohs(rtp_hdr->sequence); timestamp = ntohl(rtp_hdr->timestamp); arrival_time = get_current_ts(); + ssrc = ntohl(rtp_hdr->ssrc); if (!state->initialized) { state->in_stream.last_seq = seq - 1; - state->in_stream.ssrc = state->orig_ssrc = rtp_hdr->ssrc; + state->in_stream.ssrc = state->orig_ssrc = ssrc; state->in_stream.last_tsdelta = 0; state->base_seq = seq; state->initialized = 1; @@ -242,8 +243,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta rtp_end->frame_duration_den; state->out_stream = state->in_stream; state->out_stream.last_timestamp = timestamp; - /* force output SSRC change */ - state->out_stream.ssrc = rtp_hdr->ssrc - 1; + state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */ LOGP(DMGCP, LOGL_INFO, "Initializing stream on 0x%x SSRC: %u timestamp: %u " "pkt-duration: %d, from %s:%d in %d\n", @@ -251,7 +251,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->seq_offset, state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); - } else if (state->in_stream.ssrc != rtp_hdr->ssrc) { + } else if (state->in_stream.ssrc != ssrc) { LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed on 0x%x: %u -> %u " "from %s:%d in %d\n", @@ -260,7 +260,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); - state->in_stream.ssrc = rtp_hdr->ssrc; + state->in_stream.ssrc = ssrc; if (rtp_end->force_constant_ssrc) { int32_t tsdelta = state->out_stream.last_tsdelta; if (tsdelta == 0) { @@ -279,6 +279,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta (state->out_stream.last_timestamp + tsdelta) - timestamp; state->patch = 1; + ssrc = state->orig_ssrc; LOGP(DMGCP, LOGL_NOTICE, "SSRC patching enabled on 0x%x SSRC: %u " @@ -296,6 +297,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta check_rtp_timestamp(endp, &state->in_stream, rtp_end, addr, seq, timestamp, "input", &state->in_stream.last_tsdelta); + + if (state->patch) + ssrc = state->orig_ssrc; } /* Save before patching */ @@ -306,14 +310,14 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta if (state->patch) { seq += state->seq_offset; rtp_hdr->sequence = htons(seq); - rtp_hdr->ssrc = state->orig_ssrc; + rtp_hdr->ssrc = htonl(ssrc); timestamp += state->timestamp_offset; rtp_hdr->timestamp = htonl(timestamp); } /* Check again, whether the timestamps are still valid */ - if (state->out_stream.ssrc == rtp_hdr->ssrc) + if (state->out_stream.ssrc == ssrc) check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr, seq, timestamp, "output", &state->out_stream.last_tsdelta); @@ -350,7 +354,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta /* Save output values */ state->out_stream.last_seq = seq; state->out_stream.last_timestamp = timestamp; - state->out_stream.ssrc = rtp_hdr->ssrc; + state->out_stream.ssrc = ssrc; if (payload < 0) return; diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 908773a..e5b8bbd 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -586,7 +586,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) if (state.out_stream.ssrc != last_ssrc) { printf("Output SSRC changed to %08x\n", - ntohl(state.out_stream.ssrc)); + state.out_stream.ssrc); last_ssrc = state.out_stream.ssrc; } -- 1.7.9.5 From holger at freyther.de Mon Dec 9 16:01:05 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Mon, 9 Dec 2013 17:01:05 +0100 Subject: [PATCH 07/10] mgcp/rtp: Use SSRC in proper byte ordering In-Reply-To: <1386261865-23713-7-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-7-git-send-email-jerlbeck@sysmocom.de> Message-ID: <20131209160105.GE22686@xiaoyu.lan> On Thu, Dec 05, 2013 at 05:44:22PM +0100, Jacob Erlbeck wrote: Dear Jacob, > if (state.out_stream.ssrc != last_ssrc) { > printf("Output SSRC changed to %08x\n", > - ntohl(state.out_stream.ssrc)); > + state.out_stream.ssrc); is an update of the .ok file missing or is this case not tested by our regression suite? thanks holger From jerlbeck at sysmocom.de Tue Dec 10 11:48:02 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 10 Dec 2013 12:48:02 +0100 Subject: [PATCH 07/10] mgcp/rtp: Use SSRC in proper byte ordering In-Reply-To: <20131209160105.GE22686@xiaoyu.lan> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-7-git-send-email-jerlbeck@sysmocom.de> <20131209160105.GE22686@xiaoyu.lan> Message-ID: <52A6FF72.5030609@sysmocom.de> On 12/09/2013 05:01 PM, Holger Hans Peter Freyther wrote: > On Thu, Dec 05, 2013 at 05:44:22PM +0100, Jacob Erlbeck wrote: > >> if (state.out_stream.ssrc != last_ssrc) { >> printf("Output SSRC changed to %08x\n", >> - ntohl(state.out_stream.ssrc)); >> + state.out_stream.ssrc); > > is an update of the .ok file missing or is this case not tested > by our regression suite? The ntohl() has been moved from here to where the binary data is read (mgcp_network.c), so the .ok file remains unchanged (in opposite to the log messages). Jacob From jerlbeck at sysmocom.de Thu Dec 5 16:44:23 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:23 +0100 Subject: [PATCH 08/10] mgcp/rtp: Only patch SSRC once after MDCX if enabled In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-8-git-send-email-jerlbeck@sysmocom.de> Currently the output SSRC is always forced to be the same if SSRC patching is enabled. This patch modifies this to optionally restrict the number of SSRC changes that will be corrected. Note that the configuration only allows for the 'once' mode and 'off'. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 2 +- openbsc/src/libmgcp/mgcp_network.c | 2 ++ openbsc/src/libmgcp/mgcp_protocol.c | 4 +++- openbsc/src/libmgcp/mgcp_vty.c | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index b16bd49..255161f 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -85,7 +85,7 @@ struct mgcp_rtp_end { char *fmtp_extra; /* RTP patching */ - int force_constant_ssrc; + int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ int force_constant_timing; /* diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index abb48f5..79c9e1a 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -280,6 +280,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta timestamp; state->patch = 1; ssrc = state->orig_ssrc; + if (rtp_end->force_constant_ssrc != -1) + rtp_end->force_constant_ssrc -= 1; LOGP(DMGCP, LOGL_NOTICE, "SSRC patching enabled on 0x%x SSRC: %u " diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index d7e4745..facbeb2 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -619,8 +619,10 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, { struct mgcp_trunk_config *tcfg = endp->tcfg; + int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc; + rtp->force_constant_timing = tcfg->force_constant_timing; - rtp->force_constant_ssrc = tcfg->force_constant_ssrc; + rtp->force_constant_ssrc = patch_ssrc ? 1 : 0; } static struct msgb *handle_create_con(struct mgcp_parse_data *p) diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 16b973f..38570cb 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -613,7 +613,7 @@ DEFUN(cfg_trunk_patch_rtp_ssrc, RTP_PATCH_STR "Force a fixed SSRC\n" "Disable\n" - "Enable\n" + "Enable (once per MDCX)\n" ) { struct mgcp_trunk_config *trunk = vty->index; -- 1.7.9.5 From jerlbeck at sysmocom.de Thu Dec 5 16:44:24 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:24 +0100 Subject: [PATCH 09/10] mgcp/rtp: Fix RTP timestamps if enabled In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-9-git-send-email-jerlbeck@sysmocom.de> This forces the output timing to fulfill dTS = dSegNo * fixedPacketDuration where dSegNo = seqNo - lastSeqNo. If timestamp patching is enabled, the output timestamp will be set to lastTimestamp + dTS. This kind of relative updating is used to handle seqNo- and timestamp-wraparounds properly. The updating of timestamp and SSRC has been separated and the patch field of mgcp_rtp_state has been renamed to patch_ssrc to reflect it's semantics more closely. The offset fields are now used always and will change the corresponding header field if they are != 0. Ticket: OW#1065 Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 2 +- openbsc/src/libmgcp/mgcp_network.c | 40 +++++++++++++---- openbsc/tests/mgcp/mgcp_test.ok | 72 +++++++++++++++---------------- 3 files changed, 68 insertions(+), 46 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 255161f..b6f4a0d 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -50,7 +50,7 @@ struct mgcp_rtp_stream_state { struct mgcp_rtp_state { int initialized; - int patch; + int patch_ssrc; uint32_t orig_ssrc; diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 79c9e1a..6463ae0 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -278,7 +278,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->timestamp_offset = (state->out_stream.last_timestamp + tsdelta) - timestamp; - state->patch = 1; + state->patch_ssrc = 1; ssrc = state->orig_ssrc; if (rtp_end->force_constant_ssrc != -1) rtp_end->force_constant_ssrc -= 1; @@ -300,7 +300,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta seq, timestamp, "input", &state->in_stream.last_tsdelta); - if (state->patch) + if (state->patch_ssrc) ssrc = state->orig_ssrc; } @@ -308,16 +308,38 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->in_stream.last_timestamp = timestamp; state->in_stream.last_seq = seq; - /* apply the offset and store it back to the packet */ - if (state->patch) { - seq += state->seq_offset; - rtp_hdr->sequence = htons(seq); - rtp_hdr->ssrc = htonl(ssrc); + if (rtp_end->force_constant_timing && + state->out_stream.ssrc == ssrc && state->packet_duration) { + int delta_seq = seq + state->seq_offset - state->out_stream.last_seq; + int timestamp_offset = + state->out_stream.last_timestamp - timestamp + + delta_seq * state->packet_duration; + if (state->timestamp_offset != timestamp_offset) { + state->timestamp_offset = timestamp_offset; - timestamp += state->timestamp_offset; - rtp_hdr->timestamp = htonl(timestamp); + LOGP(DMGCP, LOGL_NOTICE, + "Timestamp patching enabled on 0x%x SSRC: %u " + "SeqNo delta: %d, TS offset: %d, " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + delta_seq, state->timestamp_offset, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } } + /* Store the updated SSRC back to the packet */ + if (state->patch_ssrc) + rtp_hdr->ssrc = htonl(ssrc); + + /* Apply the offset and store it back to the packet. + * This won't change anything if the offset is 0, so the conditional is + * omitted. */ + seq += state->seq_offset; + rtp_hdr->sequence = htons(seq); + timestamp += state->timestamp_offset; + rtp_hdr->timestamp = htonl(timestamp); + /* Check again, whether the timestamps are still valid */ if (state->out_stream.ssrc == ssrc) check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr, diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 4d3caac..3beeb59 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -94,46 +94,46 @@ Output SSRC changed to 11223344 TS: 0, dTS: 0, TS Errs: in 0, out 0 TS: 160, dTS: 160, TS Errs: in 0, out 0 TS: 320, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 1, out 1 -TS: 480, dTS: 160, TS Errs: in 1, out 1 -TS: 640, dTS: 160, TS Errs: in 1, out 1 -TS: 960, dTS: 320, TS Errs: in 2, out 2 -TS: 1120, dTS: 160, TS Errs: in 3, out 3 -TS: 1280, dTS: 160, TS Errs: in 3, out 3 -TS: 1400, dTS: 120, TS Errs: in 4, out 4 -TS: 1560, dTS: 160, TS Errs: in 5, out 5 -TS: 1720, dTS: 160, TS Errs: in 5, out 5 +TS: 480, dTS: 160, TS Errs: in 1, out 0 +TS: 640, dTS: 160, TS Errs: in 1, out 0 +TS: 800, dTS: 160, TS Errs: in 1, out 0 +TS: 960, dTS: 160, TS Errs: in 2, out 0 +TS: 1120, dTS: 160, TS Errs: in 3, out 0 +TS: 1280, dTS: 160, TS Errs: in 3, out 0 +TS: 1440, dTS: 160, TS Errs: in 4, out 0 +TS: 1600, dTS: 160, TS Errs: in 5, out 0 +TS: 1760, dTS: 160, TS Errs: in 5, out 0 Output SSRC changed to 10203040 -TS: 34688, dTS: 160, TS Errs: in 5, out 5 -TS: 34848, dTS: 160, TS Errs: in 5, out 5 -TS: 35008, dTS: 160, TS Errs: in 5, out 5 -TS: 35128, dTS: 120, TS Errs: in 6, out 6 -TS: 35288, dTS: 160, TS Errs: in 7, out 7 -TS: 35448, dTS: 160, TS Errs: in 7, out 7 -TS: 35768, dTS: 160, TS Errs: in 7, out 7 -TS: 35928, dTS: 160, TS Errs: in 7, out 7 -TS: 36088, dTS: 160, TS Errs: in 8, out 8 +TS: 34728, dTS: 160, TS Errs: in 5, out 0 +TS: 34888, dTS: 160, TS Errs: in 5, out 0 +TS: 35048, dTS: 160, TS Errs: in 5, out 0 +TS: 35208, dTS: 160, TS Errs: in 6, out 0 +TS: 35368, dTS: 160, TS Errs: in 7, out 0 +TS: 35528, dTS: 160, TS Errs: in 7, out 0 +TS: 35848, dTS: 160, TS Errs: in 7, out 0 +TS: 36008, dTS: 160, TS Errs: in 7, out 0 +TS: 36008, dTS: 160, TS Errs: in 8, out 0 Testing packet error detection, patch SSRC, patch timestamps. Output SSRC changed to 11223344 TS: 0, dTS: 0, TS Errs: in 0, out 0 TS: 160, dTS: 160, TS Errs: in 0, out 0 TS: 320, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 1, out 1 -TS: 480, dTS: 160, TS Errs: in 1, out 1 -TS: 640, dTS: 160, TS Errs: in 1, out 1 -TS: 960, dTS: 320, TS Errs: in 2, out 2 -TS: 1120, dTS: 160, TS Errs: in 3, out 3 -TS: 1280, dTS: 160, TS Errs: in 3, out 3 -TS: 1400, dTS: 120, TS Errs: in 4, out 4 -TS: 1560, dTS: 160, TS Errs: in 5, out 5 -TS: 1720, dTS: 160, TS Errs: in 5, out 5 -TS: 1880, dTS: 160, TS Errs: in 5, out 5 -TS: 2040, dTS: 160, TS Errs: in 5, out 5 -TS: 2200, dTS: 160, TS Errs: in 5, out 5 -TS: 2320, dTS: 120, TS Errs: in 6, out 6 -TS: 2480, dTS: 160, TS Errs: in 7, out 7 -TS: 2640, dTS: 160, TS Errs: in 7, out 7 -TS: 2960, dTS: 160, TS Errs: in 7, out 7 -TS: 3120, dTS: 160, TS Errs: in 7, out 7 -TS: 3280, dTS: 160, TS Errs: in 8, out 8 +TS: 480, dTS: 160, TS Errs: in 1, out 0 +TS: 640, dTS: 160, TS Errs: in 1, out 0 +TS: 800, dTS: 160, TS Errs: in 1, out 0 +TS: 960, dTS: 160, TS Errs: in 2, out 0 +TS: 1120, dTS: 160, TS Errs: in 3, out 0 +TS: 1280, dTS: 160, TS Errs: in 3, out 0 +TS: 1440, dTS: 160, TS Errs: in 4, out 0 +TS: 1600, dTS: 160, TS Errs: in 5, out 0 +TS: 1760, dTS: 160, TS Errs: in 5, out 0 +TS: 1920, dTS: 160, TS Errs: in 5, out 0 +TS: 2080, dTS: 160, TS Errs: in 5, out 0 +TS: 2240, dTS: 160, TS Errs: in 5, out 0 +TS: 2400, dTS: 160, TS Errs: in 6, out 0 +TS: 2560, dTS: 160, TS Errs: in 7, out 0 +TS: 2720, dTS: 160, TS Errs: in 7, out 0 +TS: 3040, dTS: 160, TS Errs: in 7, out 0 +TS: 3200, dTS: 160, TS Errs: in 7, out 0 +TS: 3200, dTS: 160, TS Errs: in 8, out 0 Done -- 1.7.9.5 From jerlbeck at sysmocom.de Thu Dec 5 16:44:25 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Thu, 5 Dec 2013 17:44:25 +0100 Subject: [PATCH 10/10] mgcp/rtp: Refactored packet_duration computation In-Reply-To: <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> References: <1386087429-11618-1-git-send-email-jerlbeck@sysmocom.de> <1386261865-23713-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386261865-23713-10-git-send-email-jerlbeck@sysmocom.de> Since the packet duration is given in ms with the 'ptime' RTP media attribute and also with the 'p' MGCP local connection option, the computation is changed to use this value (if present). The computation assumes, that there are N complete frames in a packet and takes into account, that the ptime value possibly had been rounded towards the next ms value (which is never the case with a frame length of exact 20ms). Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp_internal.h | 3 +++ openbsc/src/libmgcp/mgcp_network.c | 5 +---- openbsc/src/libmgcp/mgcp_protocol.c | 24 +++++++++++++++++++++--- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index b6f4a0d..d59c5d7 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -82,6 +82,7 @@ struct mgcp_rtp_end { uint32_t frame_duration_num; uint32_t frame_duration_den; int frames_per_packet; + uint32_t packet_duration_ms; char *fmtp_extra; /* RTP patching */ @@ -180,6 +181,8 @@ struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, struct mgcp_rtp_end *rtp); +uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *rtp); void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, uint32_t *expected, int *loss); diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 6463ae0..bf205d1 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -237,10 +237,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->initialized = 1; state->jitter = 0; state->transit = arrival_time - timestamp; - state->packet_duration = - rtp_end->rate * rtp_end->frames_per_packet * - rtp_end->frame_duration_num / - rtp_end->frame_duration_den; + state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end); state->out_stream = state->in_stream; state->out_stream.last_timestamp = timestamp; state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */ diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index facbeb2..3e26ad6 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -75,7 +75,7 @@ char *strline_r(char *str, char **saveptr) /* Assume audio frame length of 20ms */ #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 -#define DEFAULT_RTP_AUDIO_FRAMES_PER_PACKET 1 +#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 #define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000 static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end); @@ -625,6 +625,23 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, rtp->force_constant_ssrc = patch_ssrc ? 1 : 0; } +uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *rtp) +{ + int f = 0; + + /* Get the number of frames per channel and packet */ + if (rtp->frames_per_packet) + f = rtp->frames_per_packet; + else if (rtp->packet_duration_ms && rtp->frame_duration_num) { + int den = 1000 * rtp->frame_duration_num; + f = (rtp->packet_duration_ms * rtp->frame_duration_den + den/2) + / den; + } + + return rtp->rate * f * rtp->frame_duration_num / rtp->frame_duration_den; +} + static struct msgb *handle_create_con(struct mgcp_parse_data *p) { struct mgcp_trunk_config *tcfg; @@ -1080,8 +1097,9 @@ static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) /* Set default values */ end->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; end->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; - end->frames_per_packet = DEFAULT_RTP_AUDIO_FRAMES_PER_PACKET; - end->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; + end->frames_per_packet = 0; /* unknown */ + end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; + end->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; } static void mgcp_rtp_end_init(struct mgcp_rtp_end *end) -- 1.7.9.5 From jolly at eversberg.eu Thu Dec 5 15:02:36 2013 From: jolly at eversberg.eu (Andreas Eversberg) Date: Thu, 5 Dec 2013 16:02:36 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) Message-ID: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> Speech codings which are not supported by BTS will be removed from the bearer capability information element after parsing. This way it is not required for the MNCC application to consider support of each BTS. Only GSM full rate is supported by default. --- openbsc/include/openbsc/gsm_data_shared.h | 10 ++ openbsc/src/libbsc/bsc_vty.c | 179 +++++++++++++++++++++++++++++ openbsc/src/libmsc/gsm_04_08.c | 28 +++++ 3 files changed, 217 insertions(+), 0 deletions(-) diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h index 84d15ef..dd20b2f 100644 --- a/openbsc/include/openbsc/gsm_data_shared.h +++ b/openbsc/include/openbsc/gsm_data_shared.h @@ -143,6 +143,13 @@ struct bts_ul_meas { uint8_t inv_rssi; }; +struct bts_codec_conf { + uint8_t hr; + uint8_t efr; + uint8_t afs; + uint8_t ahs; +}; + struct amr_mode { uint8_t mode; uint8_t threshold; @@ -704,6 +711,9 @@ struct gsm_bts { /* exclude the BTS from the global RF Lock handling */ int excl_from_rf_lock; + + /* supported codecs beside FR */ + struct bts_codec_conf codec; #endif /* ROLE_BSC */ void *role; }; diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index b034981..d03cafc 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -629,6 +629,17 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) } } + vty_out(vty, " codec-support fr"); + if (bts->codec.hr) + vty_out(vty, " hr"); + if (bts->codec.efr) + vty_out(vty, " efr"); + if (bts->codec.afs) + vty_out(vty, " afs"); + if (bts->codec.ahs) + vty_out(vty, " ahs"); + vty_out(vty, "%s", VTY_NEWLINE); + config_write_bts_gprs(vty, bts); if (bts->excl_from_rf_lock) @@ -2635,6 +2646,169 @@ DEFUN(cfg_bts_no_excl_rf_lock, return CMD_SUCCESS; } +DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, + "codec-support (fr|hr|efr|afs|ahs)", + "Codec Support settings\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n") +{ + struct gsm_bts *bts = vty->index; + struct bts_codec_conf *codec = &bts->codec; + int i; + + codec->hr = 0; + codec->efr = 0; + codec->afs = 0; + codec->ahs = 0; + for (i = 0; i < 1; i++) { + if (!strcmp(argv[i], "hr")) + codec->hr = 1; + if (!strcmp(argv[i], "efr")) + codec->efr = 1; + if (!strcmp(argv[i], "afs")) + codec->afs = 1; + if (!strcmp(argv[i], "ahs")) + codec->ahs = 1; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, + "codec-support (fr|hr|efr|afs|ahs) (fr|hr|efr|afs|ahs)", + "Codec Support settings\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n") +{ + struct gsm_bts *bts = vty->index; + struct bts_codec_conf *codec = &bts->codec; + int i; + + codec->hr = 0; + codec->efr = 0; + codec->afs = 0; + codec->ahs = 0; + for (i = 0; i < 2; i++) { + if (!strcmp(argv[i], "hr")) + codec->hr = 1; + if (!strcmp(argv[i], "efr")) + codec->efr = 1; + if (!strcmp(argv[i], "afs")) + codec->afs = 1; + if (!strcmp(argv[i], "ahs")) + codec->ahs = 1; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, + "codec-support (fr|hr|efr|afs|ahs) (fr|hr|efr|afs|ahs)" + " (fr|hr|efr|afs|ahs)", + "Codec Support settings\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n") +{ + struct gsm_bts *bts = vty->index; + struct bts_codec_conf *codec = &bts->codec; + int i; + + codec->hr = 0; + codec->efr = 0; + codec->afs = 0; + codec->ahs = 0; + for (i = 0; i < 3; i++) { + if (!strcmp(argv[i], "hr")) + codec->hr = 1; + if (!strcmp(argv[i], "efr")) + codec->efr = 1; + if (!strcmp(argv[i], "afs")) + codec->afs = 1; + if (!strcmp(argv[i], "ahs")) + codec->ahs = 1; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd, + "codec-support (fr|hr|efr|afs|ahs) (fr|hr|efr|afs|ahs)" + " (fr|hr|efr|afs|ahs) (fr|hr|efr|afs|ahs)", + "Codec Support settings\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n") +{ + struct gsm_bts *bts = vty->index; + struct bts_codec_conf *codec = &bts->codec; + int i; + + codec->hr = 0; + codec->efr = 0; + codec->afs = 0; + codec->ahs = 0; + for (i = 0; i < 4; i++) { + if (!strcmp(argv[i], "hr")) + codec->hr = 1; + if (!strcmp(argv[i], "efr")) + codec->efr = 1; + if (!strcmp(argv[i], "afs")) + codec->afs = 1; + if (!strcmp(argv[i], "ahs")) + codec->ahs = 1; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_codec5, cfg_bts_codec5_cmd, + "codec-support (fr|hr|efr|afs|ahs) (fr|hr|efr|afs|ahs)" + " (fr|hr|efr|afs|ahs) (fr|hr|efr|afs|ahs) (fr|hr|efr|afs|ahs)", + "Codec Support settings\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n" + "Full Rate (mandatory)\nHalf Rate\nEnhanced Full Rate\n" + "Adaptive Multirate on TCH/F\nAdaptive Multirate on TCH/H\n") +{ + struct gsm_bts *bts = vty->index; + struct bts_codec_conf *codec = &bts->codec; + int i; + + codec->hr = 0; + codec->efr = 0; + codec->afs = 0; + codec->ahs = 0; + for (i = 0; i < 5; i++) { + if (!strcmp(argv[i], "hr")) + codec->hr = 1; + if (!strcmp(argv[i], "efr")) + codec->efr = 1; + if (!strcmp(argv[i], "afs")) + codec->afs = 1; + if (!strcmp(argv[i], "ahs")) + codec->ahs = 1; + } + + return CMD_SUCCESS; +} + #define TRX_TEXT "Radio Transceiver\n" /* per TRX configuration */ @@ -3231,6 +3405,11 @@ int bsc_vty_init(const struct log_info *cat) install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd); install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd); + install_element(BTS_NODE, &cfg_bts_codec1_cmd); + install_element(BTS_NODE, &cfg_bts_codec2_cmd); + install_element(BTS_NODE, &cfg_bts_codec3_cmd); + install_element(BTS_NODE, &cfg_bts_codec4_cmd); + install_element(BTS_NODE, &cfg_bts_codec5_cmd); install_element(BTS_NODE, &cfg_trx_cmd); install_node(&trx_node, dummy_config_write); diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 3cfc455..775fe66 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -79,6 +79,29 @@ struct gsm_lai { uint16_t lac; }; +static int apply_codec_restrictions(struct gsm_bts *bts, + struct gsm_mncc_bearer_cap *bcap) +{ + int i, j; + + /* remove unsupported speech versions from list */ + for (i = 0, j = 0; bcap->speech_ver[i] >= 0; i++) { + if (bcap->speech_ver[i] == 0) + bcap->speech_ver[j++] = 0; + if (bcap->speech_ver[i] == 2 && bts->codec.efr) + bcap->speech_ver[j++] = 2; + if (bcap->speech_ver[i] == 4 && bts->codec.afs) + bcap->speech_ver[j++] = 4; + if (bcap->speech_ver[i] == 1 && bts->codec.hr) + bcap->speech_ver[j++] = 1; + if (bcap->speech_ver[i] == 5 && bts->codec.ahs) + bcap->speech_ver[j++] = 5; + } + bcap->speech_ver[j] = -1; + + return 0; +} + static uint32_t new_callref = 0x80000001; void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg) @@ -1763,6 +1786,7 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) setup.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&setup.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &setup.bearer_cap); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { @@ -1916,6 +1940,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) call_conf.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&call_conf.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &call_conf.bearer_cap); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { @@ -2604,6 +2629,7 @@ static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); @@ -2646,6 +2672,7 @@ static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } new_cc_state(trans, GSM_CSTATE_ACTIVE); @@ -2686,6 +2713,7 @@ static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) modify.fields |= GSM48_IE_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { -- 1.7.3.4 From jolly at eversberg.eu Thu Dec 5 15:02:37 2013 From: jolly at eversberg.eu (Andreas Eversberg) Date: Thu, 5 Dec 2013 16:02:37 +0100 Subject: [PATCH 2/4] Fix: Nokia requires VTY's "oml e1" parameters also In-Reply-To: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> Message-ID: <1386255759-8600-2-git-send-email-jolly@eversberg.eu> From: Andreas Eversberg When writing config file, OML configuration must be written for all E1 based BTS, which includes Nokia *Site BTS. --- openbsc/src/libbsc/bsc_vty.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index d03cafc..af0ecf9 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -601,7 +601,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) break; case GSM_BTS_TYPE_NOKIA_SITE: vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE); - break; + /* fall through: Nokia requires "oml e1" parameters also */ default: config_write_e1_link(vty, &bts->oml_e1_link, " oml "); vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); -- 1.7.3.4 From holger at freyther.de Thu Dec 5 17:54:33 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 5 Dec 2013 18:54:33 +0100 Subject: [PATCH 2/4] Fix: Nokia requires VTY's "oml e1" parameters also In-Reply-To: <1386255759-8600-2-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-2-git-send-email-jolly@eversberg.eu> Message-ID: <20131205175432.GJ22764@xiaoyu.lan> On Thu, Dec 05, 2013 at 04:02:37PM +0100, Andreas Eversberg wrote: > case GSM_BTS_TYPE_NOKIA_SITE: > vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE); > - break; > + /* fall through: Nokia requires "oml e1" parameters also */ do you or any other Nokia BTS owner care enough to make a VTY regression test for this issue? From jolly at eversberg.eu Thu Dec 5 15:02:38 2013 From: jolly at eversberg.eu (Andreas Eversberg) Date: Thu, 5 Dec 2013 16:02:38 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> Message-ID: <1386255759-8600-3-git-send-email-jolly@eversberg.eu> From: Andreas Eversberg TS 04.06 Chapter 5.4.4.4 "Local end release procedure" states that a confirm must be sent by layer 2 when receiving a local end release request. Nokia BTS might not send it, so this patch assumes that it has been received already. In order to correctly switch a channel (handover or assignment), local end release is required. --- openbsc/include/openbsc/gsm_data.h | 12 ++++++++++++ openbsc/src/libbsc/abis_rsl.c | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletions(-) diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 8741505..7c3ca84 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -370,6 +370,18 @@ static inline int is_siemens_bts(struct gsm_bts *bts) return 0; } +static inline int is_nokia_bts(struct gsm_bts *bts) +{ + switch (bts->type) { + case GSM_BTS_TYPE_NOKIA_SITE: + return 1; + default: + break; + } + + return 0; +} + enum gsm_auth_policy gsm_auth_policy_parse(const char *arg); const char *gsm_auth_policy_name(enum gsm_auth_policy policy); diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c index 41bfcdc..b107854 100644 --- a/openbsc/src/libbsc/abis_rsl.c +++ b/openbsc/src/libbsc/abis_rsl.c @@ -867,6 +867,8 @@ int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id) return abis_rsl_sendmsg(msg); } +static void rsl_handle_release(struct gsm_lchan *lchan); + /* Chapter 8.3.7 Request the release of multiframe mode of RLL connection. This is what higher layers should call. The BTS then responds with RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE, @@ -890,7 +892,18 @@ int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, DEBUGP(DRLL, "%s RSL RLL RELEASE REQ (link_id=0x%02x, reason=%u)\n", gsm_lchan_name(lchan), link_id, release_mode); - return abis_rsl_sendmsg(msg); + abis_rsl_sendmsg(msg); + + /* Do not wait for Nokia BTS to send the confirm. */ + if (is_nokia_bts(lchan->ts->trx->bts) + && release_mode == RSL_REL_LOCAL_END) { + DEBUGP(DRLL, "Do not wait for RELease CONFirm, because Nokia" + " BTS does not send it.\n"); + lchan->sapis[link_id & 0x7] = LCHAN_SAPI_UNUSED; + rsl_handle_release(lchan); + } + + return 0; } int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) -- 1.7.3.4 From holger at freyther.de Thu Dec 5 17:55:47 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 5 Dec 2013 18:55:47 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: <1386255759-8600-3-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-3-git-send-email-jolly@eversberg.eu> Message-ID: <20131205175547.GK22764@xiaoyu.lan> On Thu, Dec 05, 2013 at 04:02:38PM +0100, Andreas Eversberg wrote: > TS 04.06 Chapter 5.4.4.4 "Local end release procedure" states that a > confirm must be sent by layer 2 when receiving a local end release > request. Nokia BTS might not send it, so this patch assumes that it has > been received already. "migh". Did you or any other owner of Nokia equipment check if a local end release was sent? Please document the product and firmware version you were using (just in case newer equipment will behave differently and we need to change the patch to be more selective) From dchardware at gmail.com Thu Dec 5 21:02:54 2013 From: dchardware at gmail.com (Sipos Csaba) Date: Thu, 5 Dec 2013 22:02:54 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: References: Message-ID: <1908530365.20131205220254@freemail.hu> Hi Andreas, I can confirm, that the patches for the local release and for the TRAU mux are working with Nokia InSite, finally the handover is available and working for us with your patches :-) The local release problem also affected the SMS service, it seems this patch also solves that problem too. I will send you the software versions on my two InSite and metrosite units to keep track and check if we can get a newer version that solves this BTS SW bug. Note that the Insite units are very old, it is more likely to get newer software for the MetroSite which is also affected by this problem. Thanks again for your hard work! BR, Csaba From holger at freyther.de Fri Dec 6 07:48:34 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Fri, 6 Dec 2013 08:48:34 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: <1386255759-8600-3-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-3-git-send-email-jolly@eversberg.eu> Message-ID: <20131206074834.GA31478@xiaoyu.lan> On Thu, Dec 05, 2013 at 04:02:38PM +0100, Andreas Eversberg wrote: > @@ -890,7 +892,18 @@ int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, > DEBUGP(DRLL, "%s RSL RLL RELEASE REQ (link_id=0x%02x, reason=%u)\n", > gsm_lchan_name(lchan), link_id, release_mode); > > - return abis_rsl_sendmsg(msg); > + abis_rsl_sendmsg(msg); > + > + /* Do not wait for Nokia BTS to send the confirm. */ > + if (is_nokia_bts(lchan->ts->trx->bts) > + && release_mode == RSL_REL_LOCAL_END) { > + DEBUGP(DRLL, "Do not wait for RELease CONFirm, because Nokia" > + " BTS does not send it.\n"); > + lchan->sapis[link_id & 0x7] = LCHAN_SAPI_UNUSED; > + rsl_handle_release(lchan); In addition to what I wrote yesterday. This is also violating the principle of the least surprise and in fact breaks the error handling release. Before: The "effect" rsl_release_request could be observed after the event loop has been reached (only then we will parse the result from the BTS). Your patch: * RLL indication for the release is missing for the NokiaBTS (that is an issue and should be resolved by moving the sapi handling code out of the RSL_MT_REL_IND case into a method) * You directly call rsl_handle_release and can get ahead of itself. This is a problem for: if (deact_sacch == SACCH_DEACTIVATE) rsl_deact_sacch(lchan); rsl_release_sapis_from(lchan, 0, RSL_REL_LOCAL_END); /* TODO: start T3109 now. */ rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR); So with your change rsl_handle_release will be called when lchan->state is not set to LCHAN_S_REL_ERR yet. holger From andreas at eversberg.eu Fri Dec 6 10:04:49 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Fri, 06 Dec 2013 11:04:49 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: <20131206074834.GA31478@xiaoyu.lan> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-3-git-send-email-jolly@eversberg.eu> <20131206074834.GA31478@xiaoyu.lan> Message-ID: <52A1A141.7020403@eversberg.eu> > > * You directly call rsl_handle_release and can get ahead of itself. This > is a problem for: > > > if (deact_sacch == SACCH_DEACTIVATE) > rsl_deact_sacch(lchan); > rsl_release_sapis_from(lchan, 0, RSL_REL_LOCAL_END); > > /* TODO: start T3109 now. */ > rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR); > > So with your change rsl_handle_release will be called when lchan->state > is not set to LCHAN_S_REL_ERR yet. > you are right. i have no solution for this problem yet. at osmocombb/mobile i use queues to solve these kind of problems. whenever i want to handle something after osmo_select_main() was processed, i write into a specific queue. the main loop will call all dequeue functions in a loop, until all queues are empty and then continue with next osmo_select_main() call. one solution for handling the missing RELease CONFirm from BTS could be a timer with 0 duration. the timer will be handled right after the process you showed above is done. this timer could also have a duration of 1 second. if the BTS acutally sends a confirm, the timer could be stopped. this way it would also work with BTS firmwares that actually send a confirm. From holger at freyther.de Fri Dec 6 13:15:59 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Fri, 6 Dec 2013 14:15:59 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: <52A1A141.7020403@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-3-git-send-email-jolly@eversberg.eu> <20131206074834.GA31478@xiaoyu.lan> <52A1A141.7020403@eversberg.eu> Message-ID: <20131206131559.GB24984@xiaoyu.lan> On Fri, Dec 06, 2013 at 11:04:49AM +0100, Andreas Eversberg wrote: > you are right. i have no solution for this problem yet. at > osmocombb/mobile i use queues to solve these kind of problems. whenever > i want to handle something after osmo_select_main() was processed, i > write into a specific queue. the main loop will call all dequeue > functions in a loop, until all queues are empty and then continue with > next osmo_select_main() call. one solution for handling the missing > RELease CONFirm from BTS could be a timer with 0 duration. the timer > will be handled right after the process you showed above is done. this > timer could also have a duration of 1 second. if the BTS acutally sends > a confirm, the timer could be stopped. this way it would also work with > BTS firmwares that actually send a confirm. Your last proposal is asking for introducing a race condition. This is a very bad idea. You have two options. You can use multiple 0 duration timers and fake the release confirm. The other option is to make the callers aware of missing release confirms. From holger at freyther.de Thu Dec 19 10:27:47 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 19 Dec 2013 11:27:47 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: <20131206131559.GB24984@xiaoyu.lan> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-3-git-send-email-jolly@eversberg.eu> <20131206074834.GA31478@xiaoyu.lan> <52A1A141.7020403@eversberg.eu> <20131206131559.GB24984@xiaoyu.lan> Message-ID: <20131219102747.GD30292@xiaoyu.lan> On Fri, Dec 06, 2013 at 02:15:59PM +0100, Holger Hans Peter Freyther wrote: Dear Andreas, > Your last proposal is asking for introducing a race condition. This > is a very bad idea. You have two options. You can use multiple 0 duration > timers and fake the release confirm. The other option is to make the > callers aware of missing release confirms. do you have an updated patch for that? The easiest would be to have a single timer per lchan and in rsl_release_sapis_from start a timer and in lchan->sapis[sapi] store a new enum value of something like LCHAN_SAPI_REL_REQUESTED? holger From andreas at eversberg.eu Thu Dec 19 11:01:39 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Thu, 19 Dec 2013 12:01:39 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: <20131219102747.GD30292@xiaoyu.lan> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-3-git-send-email-jolly@eversberg.eu> <20131206074834.GA31478@xiaoyu.lan> <52A1A141.7020403@eversberg.eu> <20131206131559.GB24984@xiaoyu.lan> <20131219102747.GD30292@xiaoyu.lan> Message-ID: <52B2D213.3030801@eversberg.eu> Holger Hans Peter Freyther wrote: >> Your last proposal is asking for introducing a race condition. This >> > is a very bad idea. You have two options. You can use multiple 0 duration >> > timers and fake the release confirm. The other option is to make the >> > callers aware of missing release confirms. >> > do you have an updated patch for that? The easiest would be to have > a single timer per lchan and in rsl_release_sapis_from start a timer > and in lchan->sapis[sapi] store a new enum value of something like > LCHAN_SAPI_REL_REQUESTED? > hi holger, i have no update yet, but i will implement your suggestion. best regards, andreas From andreas at eversberg.eu Thu Dec 19 12:40:40 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Thu, 19 Dec 2013 13:40:40 +0100 Subject: [PATCH 3/4] Fix: Nokia BTS does not send RELease CONFirm message for local end release In-Reply-To: <20131219102747.GD30292@xiaoyu.lan> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-3-git-send-email-jolly@eversberg.eu> <20131206074834.GA31478@xiaoyu.lan> <52A1A141.7020403@eversberg.eu> <20131206131559.GB24984@xiaoyu.lan> <20131219102747.GD30292@xiaoyu.lan> Message-ID: <52B2E948.3080007@eversberg.eu> Holger Hans Peter Freyther wrote: > do you have an updated patch for that? The easiest would be to have > a single timer per lchan and in rsl_release_sapis_from start a timer > and in lchan->sapis[sapi] store a new enum value of something like > LCHAN_SAPI_REL_REQUESTED? > i started timer in rsl_release_request, because there may be other users that call it with local end release. since we don't know what other BTS firmware have this release bug, i added this workaround as an option to the VTY, which is disabled by default. note that this patch is just a preview. it works but has only been tested briefly. -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: nokia_local_release.patch URL: From jolly at eversberg.eu Thu Dec 5 15:02:39 2013 From: jolly at eversberg.eu (Andreas Eversberg) Date: Thu, 5 Dec 2013 16:02:39 +0100 Subject: [PATCH 4/4] Add function to update TRAU muxer after assignment or handover In-Reply-To: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> Message-ID: <1386255759-8600-4-git-send-email-jolly@eversberg.eu> From: Andreas Eversberg E1 based BTS use TRAU muxer to decode TRAU frames. After changing channel from one timeslot to another (due to handover or assignment), the TRAU muxer must be updated. The call reference of the call is disconnected from the old channel and connected to the new channel. --- openbsc/include/openbsc/gsm_data.h | 14 ++++++++++++++ openbsc/include/openbsc/trau_mux.h | 3 +++ openbsc/src/libbsc/bsc_api.c | 5 +++++ openbsc/src/libbsc/handover_logic.c | 7 +++++-- openbsc/src/libmsc/gsm_04_08.c | 12 +++++++++--- openbsc/src/libtrau/trau_mux.c | 18 ++++++++++++++++++ 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 7c3ca84..41fe328 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -382,6 +382,20 @@ static inline int is_nokia_bts(struct gsm_bts *bts) return 0; } +static inline int is_e1_bts(struct gsm_bts *bts) +{ + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_RBS2000: + case GSM_BTS_TYPE_NOKIA_SITE: + return 1; + default: + break; + } + + return 0; +} + enum gsm_auth_policy gsm_auth_policy_parse(const char *arg); const char *gsm_auth_policy_name(enum gsm_auth_policy policy); diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h index 2c01b06..667f623 100644 --- a/openbsc/include/openbsc/trau_mux.h +++ b/openbsc/include/openbsc/trau_mux.h @@ -51,5 +51,8 @@ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref); /* send trau from application */ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame); +/* switch trau muxer to new lchan */ +int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan); + /* callback invoked if we receive TRAU frames */ int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c index 86d2493..e567038 100644 --- a/openbsc/src/libbsc/bsc_api.c +++ b/openbsc/src/libbsc/bsc_api.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -419,6 +420,10 @@ static void handle_ass_compl(struct gsm_subscriber_connection *conn, return; } + /* switch TRAU muxer for E1 based BTS from one channel to another */ + if (is_e1_bts(conn->bts)) + switch_trau_mux(conn->lchan, conn->secondary_lchan); + /* swap channels */ osmo_timer_del(&conn->T10); diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c index 9cf26af..36a758b 100644 --- a/openbsc/src/libbsc/handover_logic.c +++ b/openbsc/src/libbsc/handover_logic.c @@ -39,6 +39,7 @@ #include #include #include +#include struct bsc_handover { struct llist_head list; @@ -264,6 +265,10 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) osmo_timer_del(&ho->T3103); + /* switch TRAU muxer for E1 based BTS from one channel to another */ + if (is_e1_bts(new_lchan->conn->bts)) + switch_trau_mux(ho->old_lchan, new_lchan); + /* Replace the ho lchan with the primary one */ if (ho->old_lchan != new_lchan->conn->lchan) LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); @@ -278,8 +283,6 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); - /* do something to re-route the actual speech frames ! */ - llist_del(&ho->list); talloc_free(ho); diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 775fe66..4dedd67 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -1612,6 +1612,9 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) lchan = trans->conn->lchan; bts = lchan->ts->trx->bts; + /* store receive state */ + trans->tch_recv = enable; + switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: @@ -1619,10 +1622,8 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n"); return -EINVAL; } - /* in case, we don't have a RTP socket yet, we note this - * in the transaction and try later */ + /* in case, we don't have a RTP socket yet, we try later */ if (!lchan->abis_ip.rtp_socket) { - trans->tch_recv = enable; DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); return 0; } @@ -1641,6 +1642,11 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_RBS2000: case GSM_BTS_TYPE_NOKIA_SITE: + /* in case we don't have a TCH with correct mode, we try later */ + if (lchan->tch_mode == GSM48_CMODE_SIGN) { + DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); + return 0; + } if (enable) return trau_recv_lchan(lchan, callref); return trau_mux_unmap(NULL, callref); diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c index 9272ac0..d25e78f 100644 --- a/openbsc/src/libtrau/trau_mux.c +++ b/openbsc/src/libtrau/trau_mux.c @@ -30,6 +30,7 @@ #include #include #include +#include /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ @@ -327,3 +328,20 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, TRAU_FRAME_BITS); } + +/* switch trau muxer to new lchan */ +int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan) +{ + struct gsm_network *net = old_lchan->ts->trx->bts->network; + struct gsm_trans *trans; + + /* look up transaction with TCH frame receive enabled */ + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->conn && trans->conn->lchan == old_lchan && trans->tch_recv) { + /* switch */ + trau_recv_lchan(new_lchan, trans->callref); + } + } + + return 0; +} -- 1.7.3.4 From holger at freyther.de Thu Dec 5 17:57:26 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 5 Dec 2013 18:57:26 +0100 Subject: [PATCH 4/4] Add function to update TRAU muxer after assignment or handover In-Reply-To: <1386255759-8600-4-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-4-git-send-email-jolly@eversberg.eu> Message-ID: <20131205175726.GL22764@xiaoyu.lan> On Thu, Dec 05, 2013 at 04:02:39PM +0100, Andreas Eversberg wrote: > + /* in case we don't have a TCH with correct mode, we try later */ > + if (lchan->tch_mode == GSM48_CMODE_SIGN) { When exactly is later and how will it be retried? holger From laforge at gnumonks.org Sat Dec 7 17:03:51 2013 From: laforge at gnumonks.org (Harald Welte) Date: Sat, 7 Dec 2013 18:03:51 +0100 Subject: [PATCH 4/4] Add function to update TRAU muxer after assignment or handover In-Reply-To: <20131205175726.GL22764@xiaoyu.lan> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <1386255759-8600-4-git-send-email-jolly@eversberg.eu> <20131205175726.GL22764@xiaoyu.lan> Message-ID: <20131207170351.GA8435@nataraja.gnumonks.org> On Thu, Dec 05, 2013 at 06:57:26PM +0100, Holger Hans Peter Freyther wrote: > When exactly is later and how will it be retried? It appears to be sufficiently clear to me: When: Whenever the next frame from MNCC arrives. How: because MNCC is heanding in a stream of voice frames. Why different: Because at some point I hope that the channel mode is switched from SIGNALLING to VOICE. Regards, -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From holger at freyther.de Thu Dec 5 17:53:48 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 5 Dec 2013 18:53:48 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) In-Reply-To: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> Message-ID: <20131205175348.GI22764@xiaoyu.lan> On Thu, Dec 05, 2013 at 04:02:36PM +0100, Andreas Eversberg wrote: > +struct bts_codec_conf { > + uint8_t hr; > + uint8_t efr; > + uint8_t afs; > + uint8_t ahs; this naming appears odd. Where is it coming from? Do you know of a BTS that supports AMR on TCH/F but not on TCH/H? > +DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, > +DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, > +DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, > +DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd, > +DEFUN(cfg_bts_codec5, cfg_bts_codec5_cmd, please remove the code duplication and add a VTY testcase for setting and reading this value. > +static int apply_codec_restrictions(struct gsm_bts *bts, > + struct gsm_mncc_bearer_cap *bcap) > +{ > + int i, j; > + > + /* remove unsupported speech versions from list */ > + for (i = 0, j = 0; bcap->speech_ver[i] >= 0; i++) { > + if (bcap->speech_ver[i] == 0) > + bcap->speech_ver[j++] = 0; > + if (bcap->speech_ver[i] == 2 && bts->codec.efr) > + bcap->speech_ver[j++] = 2; > + if (bcap->speech_ver[i] == 4 && bts->codec.afs) > + bcap->speech_ver[j++] = 4; > + if (bcap->speech_ver[i] == 1 && bts->codec.hr) > + bcap->speech_ver[j++] = 1; > + if (bcap->speech_ver[i] == 5 && bts->codec.ahs) > + bcap->speech_ver[j++] = 5; > + } please use the values of the gsm48_bcap_speech_ver enum instead of the magic numbers. From andreas at eversberg.eu Fri Dec 6 09:10:44 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Fri, 06 Dec 2013 10:10:44 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) In-Reply-To: <20131205175348.GI22764@xiaoyu.lan> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <20131205175348.GI22764@xiaoyu.lan> Message-ID: <52A19494.3060400@eversberg.eu> Holger Hans Peter Freyther wrote: > > please remove the code duplication and add a VTY testcase for setting > and reading this value. dear holger, i tried to run the vty test with "make check" (without anything added by me): ... /usr/bin/python ./vty_test_runner.py -w /usr/src/openbsc/openbsc -v confpath ., workdir /usr/src/openbsc/openbsc Running tests for specific VTY commands Skipping the BSC test Skipping the NAT test testConfigNetworkTree (__main__.TestVTYNITB) ... Launch: ./src/osmo-nitb/osmo-nitb -c ./doc/examples/osmo-nitb/nanobts/openbsc.cfg from /usr/src/openbsc/openbsc the test gets stuck here. it tried "https://github.com/janfizza/osmocom-python.git" and "https://github.com/janfizza/osmocom-python.git". any hint? best regards, andreas From laforge at gnumonks.org Sat Dec 7 16:59:12 2013 From: laforge at gnumonks.org (Harald Welte) Date: Sat, 7 Dec 2013 17:59:12 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) In-Reply-To: <20131205175348.GI22764@xiaoyu.lan> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> <20131205175348.GI22764@xiaoyu.lan> Message-ID: <20131207165912.GY8435@nataraja.gnumonks.org> On Thu, Dec 05, 2013 at 06:53:48PM +0100, Holger Hans Peter Freyther wrote: > > +struct bts_codec_conf { > > + uint8_t hr; > > + uint8_t efr; > > + uint8_t afs; > > + uint8_t ahs; > > this naming appears odd. Where is it coming from? this is regular layer1 naming :) TCH/AFS, TCH/AHS is used in various parts of the spec (05.x series mainly, I would say). -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From laforge at gnumonks.org Sat Dec 7 16:56:08 2013 From: laforge at gnumonks.org (Harald Welte) Date: Sat, 7 Dec 2013 17:56:08 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) In-Reply-To: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> References: <1386255759-8600-1-git-send-email-jolly@eversberg.eu> Message-ID: <20131207165608.GX8435@nataraja.gnumonks.org> Hi Andreas, I think there is a lot of code duplication that should be fixed, like, what I'm quiting below. There are also the repeated blocks of "codec->* = 0" that should be moved into a function. Please try to avoid copy+paste style code. Regards, Harald On Thu, Dec 05, 2013 at 04:02:36PM +0100, Andreas Eversberg wrote: > @@ -629,6 +629,17 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) > } > } > > + vty_out(vty, " codec-support fr"); > + if (bts->codec.hr) > + vty_out(vty, " hr"); > + if (bts->codec.efr) > + vty_out(vty, " efr"); > + if (bts->codec.afs) > + vty_out(vty, " afs"); > + if (bts->codec.ahs) > + vty_out(vty, " ahs"); > + vty_out(vty, "%s", VTY_NEWLINE); > + > + for (i = 0; i < 1; i++) { > + if (!strcmp(argv[i], "hr")) > + codec->hr = 1; > + if (!strcmp(argv[i], "efr")) > + codec->efr = 1; > + if (!strcmp(argv[i], "afs")) > + codec->afs = 1; > + if (!strcmp(argv[i], "ahs")) > + codec->ahs = 1; > + } > + for (i = 0; i < 2; i++) { > + if (!strcmp(argv[i], "hr")) > + codec->hr = 1; > + if (!strcmp(argv[i], "efr")) > + codec->efr = 1; > + if (!strcmp(argv[i], "afs")) > + codec->afs = 1; > + if (!strcmp(argv[i], "ahs")) > + codec->ahs = 1; > + } > + for (i = 0; i < 3; i++) { > + if (!strcmp(argv[i], "hr")) > + codec->hr = 1; > + if (!strcmp(argv[i], "efr")) > + codec->efr = 1; > + if (!strcmp(argv[i], "afs")) > + codec->afs = 1; > + if (!strcmp(argv[i], "ahs")) > + codec->ahs = 1; > + } > + for (i = 0; i < 4; i++) { > + if (!strcmp(argv[i], "hr")) > + codec->hr = 1; > + if (!strcmp(argv[i], "efr")) > + codec->efr = 1; > + if (!strcmp(argv[i], "afs")) > + codec->afs = 1; > + if (!strcmp(argv[i], "ahs")) > + codec->ahs = 1; > + } > + for (i = 0; i < 5; i++) { > + if (!strcmp(argv[i], "hr")) > + codec->hr = 1; > + if (!strcmp(argv[i], "efr")) > + codec->efr = 1; > + if (!strcmp(argv[i], "afs")) > + codec->afs = 1; > + if (!strcmp(argv[i], "ahs")) > + codec->ahs = 1; -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From jerlbeck at sysmocom.de Fri Dec 6 13:39:32 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Fri, 6 Dec 2013 14:39:32 +0100 Subject: [PATCH 1/3] mgcp/rtp: Refactor timestamp offset calculation into own function Message-ID: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> Currently the timestamp offset calculation is done in two different places. This patch moves and unifies both code parts into a separate function. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 98 ++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 32 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index bf205d1..8bd8afb 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -200,6 +200,58 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, return 1; } +/* Set the timestamp offset according to the packet duration. */ +static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, + struct mgcp_rtp_state *state, + struct mgcp_rtp_end *rtp_end, + struct sockaddr_in *addr, + int16_t delta_seq, uint32_t timestamp) +{ + int32_t tsdelta = state->packet_duration; + int timestamp_offset; + + if (tsdelta == 0) { + tsdelta = state->out_stream.last_tsdelta; + if (tsdelta != 0) { + LOGP(DMGCP, LOGL_NOTICE, + "A fixed packet duration is not available on 0x%x, " + "using last output timestamp delta instead: %d " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), tsdelta, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } else { + tsdelta = rtp_end->rate * 20 / 1000; + LOGP(DMGCP, LOGL_NOTICE, + "Fixed packet duration and last timestamp delta " + "are not available on 0x%x, " + "using fixed 20ms instead: %d " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), tsdelta, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } + } + + timestamp_offset = + state->out_stream.last_timestamp - timestamp + + delta_seq * tsdelta; + + if (state->timestamp_offset != timestamp_offset) { + state->timestamp_offset = timestamp_offset; + + LOGP(DMGCP, LOGL_NOTICE, + "Timestamp offset change on 0x%x SSRC: %u " + "SeqNo delta: %d, TS offset: %d, " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + delta_seq, state->timestamp_offset, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } + + return timestamp_offset; +} /** * The RFC 3550 Appendix A assumes there are multiple sources but @@ -259,22 +311,14 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->in_stream.ssrc = ssrc; if (rtp_end->force_constant_ssrc) { - int32_t tsdelta = state->out_stream.last_tsdelta; - if (tsdelta == 0) { - tsdelta = state->packet_duration; - LOGP(DMGCP, LOGL_NOTICE, - "Timestamp delta is not available on 0x%x, " - "using packet duration instead: %d " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), tsdelta, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } + const int16_t delta_seq = 1; + state->seq_offset = - (state->out_stream.last_seq + 1) - seq; - state->timestamp_offset = - (state->out_stream.last_timestamp + tsdelta) - - timestamp; + (state->out_stream.last_seq + delta_seq) - seq; + + adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, + delta_seq, timestamp); + state->patch_ssrc = 1; ssrc = state->orig_ssrc; if (rtp_end->force_constant_ssrc != -1) @@ -282,10 +326,10 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta LOGP(DMGCP, LOGL_NOTICE, "SSRC patching enabled on 0x%x SSRC: %u " - "offset: %d tsdelta: %d " + "SeqNo offset: %d, TS offset: %d " "from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, tsdelta, + state->seq_offset, state->timestamp_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } @@ -307,22 +351,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta if (rtp_end->force_constant_timing && state->out_stream.ssrc == ssrc && state->packet_duration) { - int delta_seq = seq + state->seq_offset - state->out_stream.last_seq; - int timestamp_offset = - state->out_stream.last_timestamp - timestamp + - delta_seq * state->packet_duration; - if (state->timestamp_offset != timestamp_offset) { - state->timestamp_offset = timestamp_offset; + /* Update the timestamp offset */ + const int delta_seq = + seq + state->seq_offset - state->out_stream.last_seq; - LOGP(DMGCP, LOGL_NOTICE, - "Timestamp patching enabled on 0x%x SSRC: %u " - "SeqNo delta: %d, TS offset: %d, " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - delta_seq, state->timestamp_offset, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } + adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, + delta_seq, timestamp); } /* Store the updated SSRC back to the packet */ -- 1.7.9.5 From jerlbeck at sysmocom.de Fri Dec 6 13:39:33 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Fri, 6 Dec 2013 14:39:33 +0100 Subject: [PATCH 2/3] mgcp/test: Output the packet duration after MGCP parsing In-Reply-To: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> References: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386337174-11058-2-git-send-email-jerlbeck@sysmocom.de> This also adds additional MDCX tests (based on MDCX4) to test the analysis of different combinations of 'p' and 'ptime' fields. Sponsored-by: On-Waves ehf --- openbsc/tests/mgcp/mgcp_test.c | 68 ++++++++++++++++++++++++++++++++++++--- openbsc/tests/mgcp/mgcp_test.ok | 12 +++++++ 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index e5b8bbd..11e4b56 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -92,8 +92,9 @@ static void test_strline(void) "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ - "a=rtpmap:99 AMR/8000\r\n" -#define MDCX4_RET "200 18983216 OK\r\n" \ + "a=rtpmap:99 AMR/8000\r\n" \ + "a=ptime:40\r\n" +#define MDCX4_RET(Ident) "200 " Ident " OK\r\n" \ "I: 1\n" \ "\n" \ "v=0\r\n" \ @@ -103,6 +104,45 @@ static void test_strline(void) "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" +#define MDCX4_PT1 "MDCX 18983217 1 at mgw MGCP 1.0\r\n" \ + "C: 2\r\n" \ + "I: 1\r\n" \ + "L: p:20-40, a:AMR, nt:IN\r\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 4441 RTP/AVP 99\r\n" \ + "a=rtpmap:99 AMR/8000\r\n" \ + "a=ptime:40\r\n" + +#define MDCX4_PT2 "MDCX 18983218 1 at mgw MGCP 1.0\r\n" \ + "C: 2\r\n" \ + "I: 1\r\n" \ + "L: p:20-20, a:AMR, nt:IN\r\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 4441 RTP/AVP 99\r\n" \ + "a=rtpmap:99 AMR/8000\r\n" \ + "a=ptime:40\r\n" + +#define MDCX4_PT3 "MDCX 18983219 1 at mgw MGCP 1.0\r\n" \ + "C: 2\r\n" \ + "I: 1\r\n" \ + "L: a:AMR, nt:IN\r\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 4441 RTP/AVP 99\r\n" \ + "a=rtpmap:99 AMR/8000\r\n" \ + "a=ptime:40\r\n" + #define SHORT2 "CRCX 1" #define SHORT2_RET "510 000000 FAIL\r\n" #define SHORT3 "CRCX 1 1 at mgw" @@ -112,11 +152,13 @@ static void test_strline(void) #define CRCX "CRCX 2 1 at mgw MGCP 1.0\r\n" \ "M: sendrecv\r\n" \ "C: 2\r\n" \ + "L: p:20\r\n" \ "\r\n" \ "v=0\r\n" \ "c=IN IP4 123.12.12.123\r\n" \ "m=audio 5904 RTP/AVP 97\r\n" \ - "a=rtpmap:97 GSM-EFR/8000\r\n" + "a=rtpmap:97 GSM-EFR/8000\r\n" \ + "a=ptime:40\r\n" #define CRCX_RET "200 2 OK\r\n" \ "I: 1\n" \ @@ -128,7 +170,6 @@ static void test_strline(void) "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" - #define CRCX_ZYN "CRCX 2 1 at mgw MGCP 1.0\r" \ "M: sendrecv\r" \ "C: 2\r\r" \ @@ -184,7 +225,10 @@ static const struct mgcp_test tests[] = { { "MDCX2", MDCX_UNALLOCATED, MDCX_RET }, { "CRCX", CRCX, CRCX_RET, 97, 126 }, { "MDCX3", MDCX3, MDCX3_RET, PTYPE_NONE, 126 }, - { "MDCX4", MDCX4, MDCX4_RET, 99, 126 }, + { "MDCX4", MDCX4, MDCX4_RET("18983216"), 99, 126 }, + { "MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983217"), 99, 126 }, + { "MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99, 126 }, + { "MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99, 126 }, { "DLCX", DLCX, DLCX_RET, -1, -1 }, { "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97, 126 }, { "EMPTY", EMPTY, EMPTY_RET }, @@ -245,6 +289,7 @@ static void test_messages(void) for (i = 0; i < cfg->trunk.number_endpoints; i++) { endp = &cfg->trunk.endpoints[i]; endp->net_end.payload_type = PTYPE_NONE; + endp->net_end.packet_duration_ms = -1; } for (i = 0; i < ARRAY_SIZE(tests); i++) { @@ -266,6 +311,19 @@ static void test_messages(void) printf("%s failed '%s'\n", t->name, (char *) msg->data); msgb_free(msg); + if (last_endpoint != -1) { + endp = &cfg->trunk.endpoints[last_endpoint]; + + if (endp->net_end.packet_duration_ms != -1) + printf("Detected packet duration: %d\n", + endp->net_end.packet_duration_ms); + else + printf("Packet duration not set\n"); + + endp->net_end.packet_duration_ms = -1; + } + + /* Check detected payload type */ if (t->exp_net_ptype != PTYPE_IGNORE || t->exp_bts_ptype != PTYPE_IGNORE) { diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 3beeb59..5c6fe37 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -16,10 +16,21 @@ Testing AUEP2 Testing MDCX1 Testing MDCX2 Testing CRCX +Packet duration not set Testing MDCX3 +Packet duration not set Testing MDCX4 +Packet duration not set +Testing MDCX4_PT1 +Packet duration not set +Testing MDCX4_PT2 +Packet duration not set +Testing MDCX4_PT3 +Packet duration not set Testing DLCX +Detected packet duration: 20 Testing CRCX_ZYN +Packet duration not set Testing EMPTY Testing SHORT1 Testing SHORT2 @@ -28,6 +39,7 @@ Testing SHORT4 Testing RQNT1 Testing RQNT2 Testing DLCX +Detected packet duration: 20 Testing CRCX Re-transmitting CRCX Testing RQNT1 -- 1.7.9.5 From jerlbeck at sysmocom.de Fri Dec 6 13:39:34 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Fri, 6 Dec 2013 14:39:34 +0100 Subject: [PATCH 3/3] mgcp: Parse MGCP and SDP to get rate and packet duration In-Reply-To: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> References: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386337174-11058-3-git-send-email-jerlbeck@sysmocom.de> This patch parses the MGCP 'p' value of the local connection options, the 'ptime' and 'maxptime' SDP attributes, and the SDP rate information and sets up packet_duration_ms accordingly. The MGCP 'p' has priority over the values derived from the SDP data. If the packet duration is unknown or allows for different values (e.g. because 'p' uses a range or maxptime allows for more than one frame) the duration is set to 0. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 9 ++++ openbsc/src/libmgcp/mgcp_protocol.c | 89 +++++++++++++++++++++++++++++++---- openbsc/tests/mgcp/mgcp_test.c | 4 ++ openbsc/tests/mgcp/mgcp_test.ok | 14 +++--- 4 files changed, 100 insertions(+), 16 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 8bd8afb..40227af 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -300,6 +300,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->seq_offset, state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); + if (state->packet_duration == 0 && rtp_end->force_constant_timing) + LOGP(DMGCP, LOGL_ERROR, + "Cannot patch timestamps on 0x%x: " + "RTP packet duration is unknown, SSRC: %u, " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } else if (state->in_stream.ssrc != ssrc) { LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed on 0x%x: %u -> %u " diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 3e26ad6..ba53df1 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -75,7 +75,7 @@ char *strline_r(char *str, char **saveptr) /* Assume audio frame length of 20ms */ #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 -#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 +#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 0 /* we don't know */ #define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000 static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end); @@ -562,25 +562,64 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) { char *line; int found_media = 0; + int audio_payload = -1; for_each_line(line, p->save) { switch (line[0]) { - case 'a': case 'o': case 's': case 't': case 'v': /* skip these SDP attributes */ break; + case 'a': { + int payload; + int rate; + int channels = 1; + int ptime, ptime2 = 0; + char audio_name[64]; + char audio_codec[64]; + + if (audio_payload == -1) + break; + + if (sscanf(line, "a=rtpmap:%d %64s", + &payload, audio_name) == 2) { + if (payload != audio_payload) + break; + + if (sscanf(audio_name, "%[^/]/%d/%d", + audio_codec, &rate, &channels) < 2) + break; + + rtp->rate = rate; + if (channels != 1) + LOGP(DMGCP, LOGL_NOTICE, + "Channels != 1 in SDP: '%s' on 0x%x\n", + line, ENDPOINT_NUMBER(p->endp)); + } else if (sscanf(line, "a=ptime:%d-%d", + &ptime, &ptime2) >= 1) { + if (ptime2 > 0 && ptime2 != ptime) + rtp->packet_duration_ms = 0; + else + rtp->packet_duration_ms = ptime; + } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) { + if (ptime2 * rtp->frame_duration_den > + rtp->frame_duration_num * 1500) + /* more than 1 frame */ + rtp->packet_duration_ms = 0; + } + break; + } case 'm': { int port; - int payload; + audio_payload = -1; if (sscanf(line, "m=audio %d RTP/AVP %d", - &port, &payload) == 2) { + &port, &audio_payload) == 2) { rtp->rtp_port = htons(port); rtp->rtcp_port = htons(port + 1); - rtp->payload_type = payload; + rtp->payload_type = audio_payload; found_media = 1; } break; @@ -608,12 +647,33 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) if (found_media) LOGP(DMGCP, LOGL_NOTICE, - "Got media info via SDP: port %d, payload %d, addr %s\n", - ntohs(rtp->rtp_port), rtp->payload_type, inet_ntoa(rtp->addr)); + "Got media info via SDP: port %d, payload %d, " + "duration %d, addr %s\n", + ntohs(rtp->rtp_port), rtp->payload_type, + rtp->packet_duration_ms, inet_ntoa(rtp->addr)); return found_media; } +static void parse_local_cx_options(struct mgcp_rtp_end *rtp, const char *options) +{ + int ptime, ptime2 = 0; + char *p_opt = strstr(options, "p:"); + if (p_opt && sscanf(p_opt, "p:%d-%d", &ptime, &ptime2) >= 1) { + if (ptime2 > 0 && ptime2 != ptime) + ptime = 0; + + if (rtp->packet_duration_ms && + rtp->packet_duration_ms != ptime) + LOGP(DMGCP, LOGL_NOTICE, + "CRCX inconsistent ptime: SDP: %d, L: %d " + "(will use %d)\n", + rtp->packet_duration_ms, ptime, ptime); + + rtp->packet_duration_ms = ptime; + } +} + void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, struct mgcp_rtp_end *rtp) { @@ -707,7 +767,8 @@ mgcp_header_done: endp->callid = talloc_strdup(tcfg->endpoints, callid); if (local_options) - endp->local_options = talloc_strdup(tcfg->endpoints, local_options); + endp->local_options = talloc_strdup(tcfg->endpoints, + local_options); if (parse_conn_mode(mode, &endp->conn_mode) != 0) { error_code = 517; @@ -740,6 +801,9 @@ mgcp_header_done: if (have_sdp) parse_sdp_data(&endp->net_end, p); + if (local_options) + parse_local_cx_options(&endp->net_end, local_options); + /* policy CB */ if (p->cfg->policy_cb) { int rc; @@ -783,6 +847,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) int error_code = 500; int silent = 0; char *line; + const char *local_options = NULL; if (p->found != 0) return create_err_response(NULL, 510, "MDCX", p->trans); @@ -806,7 +871,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) break; } case 'L': - /* skip */ + local_options = (const char *) line + 3; break; case 'M': if (parse_conn_mode(line + 3, &endp->conn_mode) != 0) { @@ -832,6 +897,12 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) } } + if (local_options) { + endp->local_options = talloc_strdup(endp->tcfg->endpoints, + local_options); + parse_local_cx_options(&endp->net_end, local_options); + } + /* policy CB */ if (p->cfg->policy_cb) { int rc; diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 11e4b56..fb056e6 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -628,6 +628,10 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) * trunk.endpoints are set up properly. */ mgcp_free_endp(&endp); + /* There is no MGCP/SDP around, so set this by hand to make timestamp + * patching possible */ + rtp->packet_duration_ms = 20; + rtp->payload_type = 98; for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 5c6fe37..32af3b5 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -16,19 +16,19 @@ Testing AUEP2 Testing MDCX1 Testing MDCX2 Testing CRCX -Packet duration not set +Detected packet duration: 20 Testing MDCX3 Packet duration not set Testing MDCX4 -Packet duration not set +Detected packet duration: 20 Testing MDCX4_PT1 -Packet duration not set +Detected packet duration: 0 Testing MDCX4_PT2 -Packet duration not set +Detected packet duration: 20 Testing MDCX4_PT3 -Packet duration not set +Detected packet duration: 40 Testing DLCX -Detected packet duration: 20 +Detected packet duration: 0 Testing CRCX_ZYN Packet duration not set Testing EMPTY @@ -39,7 +39,7 @@ Testing SHORT4 Testing RQNT1 Testing RQNT2 Testing DLCX -Detected packet duration: 20 +Detected packet duration: 0 Testing CRCX Re-transmitting CRCX Testing RQNT1 -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 10 14:02:15 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 10 Dec 2013 15:02:15 +0100 Subject: [PATCH 1/5] mgcp/rtp: Refactor timestamp offset calculation into own function In-Reply-To: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> References: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> Currently the timestamp offset calculation is done in two different places. This patch moves and unifies both code parts into a separate function. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 98 ++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 32 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index bf205d1..8bd8afb 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -200,6 +200,58 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, return 1; } +/* Set the timestamp offset according to the packet duration. */ +static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, + struct mgcp_rtp_state *state, + struct mgcp_rtp_end *rtp_end, + struct sockaddr_in *addr, + int16_t delta_seq, uint32_t timestamp) +{ + int32_t tsdelta = state->packet_duration; + int timestamp_offset; + + if (tsdelta == 0) { + tsdelta = state->out_stream.last_tsdelta; + if (tsdelta != 0) { + LOGP(DMGCP, LOGL_NOTICE, + "A fixed packet duration is not available on 0x%x, " + "using last output timestamp delta instead: %d " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), tsdelta, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } else { + tsdelta = rtp_end->rate * 20 / 1000; + LOGP(DMGCP, LOGL_NOTICE, + "Fixed packet duration and last timestamp delta " + "are not available on 0x%x, " + "using fixed 20ms instead: %d " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), tsdelta, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } + } + + timestamp_offset = + state->out_stream.last_timestamp - timestamp + + delta_seq * tsdelta; + + if (state->timestamp_offset != timestamp_offset) { + state->timestamp_offset = timestamp_offset; + + LOGP(DMGCP, LOGL_NOTICE, + "Timestamp offset change on 0x%x SSRC: %u " + "SeqNo delta: %d, TS offset: %d, " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + delta_seq, state->timestamp_offset, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } + + return timestamp_offset; +} /** * The RFC 3550 Appendix A assumes there are multiple sources but @@ -259,22 +311,14 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->in_stream.ssrc = ssrc; if (rtp_end->force_constant_ssrc) { - int32_t tsdelta = state->out_stream.last_tsdelta; - if (tsdelta == 0) { - tsdelta = state->packet_duration; - LOGP(DMGCP, LOGL_NOTICE, - "Timestamp delta is not available on 0x%x, " - "using packet duration instead: %d " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), tsdelta, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } + const int16_t delta_seq = 1; + state->seq_offset = - (state->out_stream.last_seq + 1) - seq; - state->timestamp_offset = - (state->out_stream.last_timestamp + tsdelta) - - timestamp; + (state->out_stream.last_seq + delta_seq) - seq; + + adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, + delta_seq, timestamp); + state->patch_ssrc = 1; ssrc = state->orig_ssrc; if (rtp_end->force_constant_ssrc != -1) @@ -282,10 +326,10 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta LOGP(DMGCP, LOGL_NOTICE, "SSRC patching enabled on 0x%x SSRC: %u " - "offset: %d tsdelta: %d " + "SeqNo offset: %d, TS offset: %d " "from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, tsdelta, + state->seq_offset, state->timestamp_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } @@ -307,22 +351,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta if (rtp_end->force_constant_timing && state->out_stream.ssrc == ssrc && state->packet_duration) { - int delta_seq = seq + state->seq_offset - state->out_stream.last_seq; - int timestamp_offset = - state->out_stream.last_timestamp - timestamp + - delta_seq * state->packet_duration; - if (state->timestamp_offset != timestamp_offset) { - state->timestamp_offset = timestamp_offset; + /* Update the timestamp offset */ + const int delta_seq = + seq + state->seq_offset - state->out_stream.last_seq; - LOGP(DMGCP, LOGL_NOTICE, - "Timestamp patching enabled on 0x%x SSRC: %u " - "SeqNo delta: %d, TS offset: %d, " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - delta_seq, state->timestamp_offset, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } + adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, + delta_seq, timestamp); } /* Store the updated SSRC back to the packet */ -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 10 14:02:16 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 10 Dec 2013 15:02:16 +0100 Subject: [PATCH 2/5] mgcp/test: Output the packet duration after MGCP parsing In-Reply-To: <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> References: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386684139-8093-2-git-send-email-jerlbeck@sysmocom.de> This also adds additional MDCX tests (based on MDCX4) to test the analysis of different combinations of 'p' and 'ptime' fields. Sponsored-by: On-Waves ehf --- openbsc/tests/mgcp/mgcp_test.c | 69 ++++++++++++++++++++++++++++++++++++--- openbsc/tests/mgcp/mgcp_test.ok | 21 ++++++++++++ 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index e5b8bbd..1b183fd 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -92,8 +92,9 @@ static void test_strline(void) "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ - "a=rtpmap:99 AMR/8000\r\n" -#define MDCX4_RET "200 18983216 OK\r\n" \ + "a=rtpmap:99 AMR/8000\r\n" \ + "a=ptime:40\r\n" +#define MDCX4_RET(Ident) "200 " Ident " OK\r\n" \ "I: 1\n" \ "\n" \ "v=0\r\n" \ @@ -103,6 +104,45 @@ static void test_strline(void) "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" +#define MDCX4_PT1 "MDCX 18983217 1 at mgw MGCP 1.0\r\n" \ + "C: 2\r\n" \ + "I: 1\r\n" \ + "L: p:20-40, a:AMR, nt:IN\r\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 4441 RTP/AVP 99\r\n" \ + "a=rtpmap:99 AMR/8000\r\n" \ + "a=ptime:40\r\n" + +#define MDCX4_PT2 "MDCX 18983218 1 at mgw MGCP 1.0\r\n" \ + "C: 2\r\n" \ + "I: 1\r\n" \ + "L: p:20-20, a:AMR, nt:IN\r\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 4441 RTP/AVP 99\r\n" \ + "a=rtpmap:99 AMR/8000\r\n" \ + "a=ptime:40\r\n" + +#define MDCX4_PT3 "MDCX 18983219 1 at mgw MGCP 1.0\r\n" \ + "C: 2\r\n" \ + "I: 1\r\n" \ + "L: a:AMR, nt:IN\r\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 4441 RTP/AVP 99\r\n" \ + "a=rtpmap:99 AMR/8000\r\n" \ + "a=ptime:40\r\n" + #define SHORT2 "CRCX 1" #define SHORT2_RET "510 000000 FAIL\r\n" #define SHORT3 "CRCX 1 1 at mgw" @@ -112,11 +152,13 @@ static void test_strline(void) #define CRCX "CRCX 2 1 at mgw MGCP 1.0\r\n" \ "M: sendrecv\r\n" \ "C: 2\r\n" \ + "L: p:20\r\n" \ "\r\n" \ "v=0\r\n" \ "c=IN IP4 123.12.12.123\r\n" \ "m=audio 5904 RTP/AVP 97\r\n" \ - "a=rtpmap:97 GSM-EFR/8000\r\n" + "a=rtpmap:97 GSM-EFR/8000\r\n" \ + "a=ptime:40\r\n" #define CRCX_RET "200 2 OK\r\n" \ "I: 1\n" \ @@ -128,7 +170,6 @@ static void test_strline(void) "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" - #define CRCX_ZYN "CRCX 2 1 at mgw MGCP 1.0\r" \ "M: sendrecv\r" \ "C: 2\r\r" \ @@ -184,7 +225,10 @@ static const struct mgcp_test tests[] = { { "MDCX2", MDCX_UNALLOCATED, MDCX_RET }, { "CRCX", CRCX, CRCX_RET, 97, 126 }, { "MDCX3", MDCX3, MDCX3_RET, PTYPE_NONE, 126 }, - { "MDCX4", MDCX4, MDCX4_RET, 99, 126 }, + { "MDCX4", MDCX4, MDCX4_RET("18983216"), 99, 126 }, + { "MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983217"), 99, 126 }, + { "MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99, 126 }, + { "MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99, 126 }, { "DLCX", DLCX, DLCX_RET, -1, -1 }, { "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97, 126 }, { "EMPTY", EMPTY, EMPTY_RET }, @@ -245,6 +289,7 @@ static void test_messages(void) for (i = 0; i < cfg->trunk.number_endpoints; i++) { endp = &cfg->trunk.endpoints[i]; endp->net_end.payload_type = PTYPE_NONE; + endp->net_end.packet_duration_ms = -1; } for (i = 0; i < ARRAY_SIZE(tests); i++) { @@ -266,6 +311,20 @@ static void test_messages(void) printf("%s failed '%s'\n", t->name, (char *) msg->data); msgb_free(msg); + if (last_endpoint != -1) { + endp = &cfg->trunk.endpoints[last_endpoint]; + + if (endp->net_end.packet_duration_ms != -1) + printf("Detected packet duration: %d\n", + endp->net_end.packet_duration_ms); + else + printf("Packet duration not set\n"); + printf("Requested packetization period not set\n"); + + endp->net_end.packet_duration_ms = -1; + } + + /* Check detected payload type */ if (t->exp_net_ptype != PTYPE_IGNORE || t->exp_bts_ptype != PTYPE_IGNORE) { diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 3beeb59..509958a 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -16,10 +16,29 @@ Testing AUEP2 Testing MDCX1 Testing MDCX2 Testing CRCX +Packet duration not set +Requested packetization period not set Testing MDCX3 +Packet duration not set +Requested packetization period not set Testing MDCX4 +Packet duration not set +Requested packetization period not set +Testing MDCX4_PT1 +Packet duration not set +Requested packetization period not set +Testing MDCX4_PT2 +Packet duration not set +Requested packetization period not set +Testing MDCX4_PT3 +Packet duration not set +Requested packetization period not set Testing DLCX +Detected packet duration: 20 +Requested packetization period not set Testing CRCX_ZYN +Packet duration not set +Requested packetization period not set Testing EMPTY Testing SHORT1 Testing SHORT2 @@ -28,6 +47,8 @@ Testing SHORT4 Testing RQNT1 Testing RQNT2 Testing DLCX +Detected packet duration: 20 +Requested packetization period not set Testing CRCX Re-transmitting CRCX Testing RQNT1 -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 10 14:02:17 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 10 Dec 2013 15:02:17 +0100 Subject: [PATCH 3/5] mgcp: Put local connection options into a struct In-Reply-To: <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> References: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386684139-8093-3-git-send-email-jerlbeck@sysmocom.de> Currently the local connection options have been stored as a string. This patch replaces this string by a struct (that still contains a string) along with the parsed fields (only the packetization period at the moment). It also re-adds the calls to set_local_cx_options() to the handle_create_con() and handle_modify_con() functions. Except for the test program this has no side effects, since the LCO values aren't used yet. --- openbsc/include/openbsc/mgcp_internal.h | 8 ++++++- openbsc/src/libmgcp/mgcp_protocol.c | 37 ++++++++++++++++++++++++++----- openbsc/tests/mgcp/mgcp_test.c | 9 +++++++- openbsc/tests/mgcp/mgcp_test.ok | 8 +++---- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index d59c5d7..20c433a 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -114,6 +114,12 @@ struct mgcp_rtp_tap { struct sockaddr_in forward; }; +struct mgcp_lco { + char *string; + int pkt_period_min; /* time in ms */ + int pkt_period_max; /* time in ms */ +}; + enum mgcp_type { MGCP_RTP_DEFAULT = 0, MGCP_RTP_TRANSCODED, @@ -123,7 +129,7 @@ struct mgcp_endpoint { int allocated; uint32_t ci; char *callid; - char *local_options; + struct mgcp_lco local_options; int conn_mode; int orig_mode; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index ab94164..5403861 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -614,6 +614,29 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) return found_media; } +/* Set the LCO from a string (see RFC 3435). + * The string is stored in the 'string' field. A NULL string is handled excatly + * like an empty string, the 'string' field is never NULL after this function + * has been called. */ +static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, + const char *options, int size) +{ + char *p_opt; + + talloc_free(lco->string); + lco->pkt_period_min = lco->pkt_period_max = 0; + + if (size >= 0) + lco->string = talloc_strndup(ctx, options, size); + else + lco->string = talloc_strdup(ctx, options ? options : ""); + + p_opt = strstr(lco->string, "p:"); + if (p_opt && sscanf(p_opt, "p:%d-%d", + &lco->pkt_period_min, &lco->pkt_period_max) == 1) + lco->pkt_period_max = lco->pkt_period_min; +} + void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, struct mgcp_rtp_end *rtp) { @@ -712,8 +735,8 @@ mgcp_header_done: /* copy some parameters */ endp->callid = talloc_strdup(tcfg->endpoints, callid); - if (local_options) - endp->local_options = talloc_strdup(tcfg->endpoints, local_options); + set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, + local_options, -1); if (parse_conn_mode(mode, &endp->conn_mode) != 0) { error_code = 517; @@ -789,6 +812,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) int error_code = 500; int silent = 0; char *line; + const char *local_options = NULL; if (p->found != 0) return create_err_response(NULL, 510, "MDCX", p->trans); @@ -812,7 +836,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) break; } case 'L': - /* skip */ + local_options = (const char *) line + 3; break; case 'M': if (parse_conn_mode(line + 3, &endp->conn_mode) != 0) { @@ -838,6 +862,9 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) } } + set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, + local_options, -1); + /* policy CB */ if (p->cfg->policy_cb) { int rc; @@ -1148,8 +1175,8 @@ void mgcp_free_endp(struct mgcp_endpoint *endp) talloc_free(endp->callid); endp->callid = NULL; - talloc_free(endp->local_options); - endp->local_options = NULL; + talloc_free(endp->local_options.string); + endp->local_options.string = NULL; mgcp_rtp_end_reset(&endp->bts_end); mgcp_rtp_end_reset(&endp->net_end); diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 1b183fd..3591124 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -319,7 +319,14 @@ static void test_messages(void) endp->net_end.packet_duration_ms); else printf("Packet duration not set\n"); - printf("Requested packetization period not set\n"); + if (endp->local_options.pkt_period_min || + endp->local_options.pkt_period_max) + printf("Requested packetetization period: " + "%d-%d\n", + endp->local_options.pkt_period_min, + endp->local_options.pkt_period_max); + else + printf("Requested packetization period not set\n"); endp->net_end.packet_duration_ms = -1; } diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 509958a..24f9b33 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -17,19 +17,19 @@ Testing MDCX1 Testing MDCX2 Testing CRCX Packet duration not set -Requested packetization period not set +Requested packetetization period: 20-20 Testing MDCX3 Packet duration not set Requested packetization period not set Testing MDCX4 Packet duration not set -Requested packetization period not set +Requested packetetization period: 20-20 Testing MDCX4_PT1 Packet duration not set -Requested packetization period not set +Requested packetetization period: 20-40 Testing MDCX4_PT2 Packet duration not set -Requested packetization period not set +Requested packetetization period: 20-20 Testing MDCX4_PT3 Packet duration not set Requested packetization period not set -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 10 14:02:18 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 10 Dec 2013 15:02:18 +0100 Subject: [PATCH 4/5] mgcp: Parse SDP to get rate and packet duration In-Reply-To: <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> References: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386684139-8093-4-git-send-email-jerlbeck@sysmocom.de> This patch parses the 'ptime' and 'maxptime' SDP attributes, and the SDP rate information and sets up packet_duration_ms accordingly. If the packet duration is unknown or allows for different values (e.g. because 'ptime' uses a range or 'maxptime' allows for more than one frame) the duration is set to 0. Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 9 ++++++ openbsc/src/libmgcp/mgcp_protocol.c | 53 +++++++++++++++++++++++++++++++---- openbsc/tests/mgcp/mgcp_test.c | 4 +++ openbsc/tests/mgcp/mgcp_test.ok | 10 +++---- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 8bd8afb..40227af 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -300,6 +300,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->seq_offset, state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); + if (state->packet_duration == 0 && rtp_end->force_constant_timing) + LOGP(DMGCP, LOGL_ERROR, + "Cannot patch timestamps on 0x%x: " + "RTP packet duration is unknown, SSRC: %u, " + "from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } else if (state->in_stream.ssrc != ssrc) { LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed on 0x%x: %u -> %u " diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 5403861..b9e1382 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -562,25 +562,64 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) { char *line; int found_media = 0; + int audio_payload = -1; for_each_line(line, p->save) { switch (line[0]) { - case 'a': case 'o': case 's': case 't': case 'v': /* skip these SDP attributes */ break; + case 'a': { + int payload; + int rate; + int channels = 1; + int ptime, ptime2 = 0; + char audio_name[64]; + char audio_codec[64]; + + if (audio_payload == -1) + break; + + if (sscanf(line, "a=rtpmap:%d %64s", + &payload, audio_name) == 2) { + if (payload != audio_payload) + break; + + if (sscanf(audio_name, "%[^/]/%d/%d", + audio_codec, &rate, &channels) < 2) + break; + + rtp->rate = rate; + if (channels != 1) + LOGP(DMGCP, LOGL_NOTICE, + "Channels != 1 in SDP: '%s' on 0x%x\n", + line, ENDPOINT_NUMBER(p->endp)); + } else if (sscanf(line, "a=ptime:%d-%d", + &ptime, &ptime2) >= 1) { + if (ptime2 > 0 && ptime2 != ptime) + rtp->packet_duration_ms = 0; + else + rtp->packet_duration_ms = ptime; + } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) { + if (ptime2 * rtp->frame_duration_den > + rtp->frame_duration_num * 1500) + /* more than 1 frame */ + rtp->packet_duration_ms = 0; + } + break; + } case 'm': { int port; - int payload; + audio_payload = -1; if (sscanf(line, "m=audio %d RTP/AVP %d", - &port, &payload) == 2) { + &port, &audio_payload) == 2) { rtp->rtp_port = htons(port); rtp->rtcp_port = htons(port + 1); - rtp->payload_type = payload; + rtp->payload_type = audio_payload; found_media = 1; } break; @@ -608,8 +647,10 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) if (found_media) LOGP(DMGCP, LOGL_NOTICE, - "Got media info via SDP: port %d, payload %d, addr %s\n", - ntohs(rtp->rtp_port), rtp->payload_type, inet_ntoa(rtp->addr)); + "Got media info via SDP: port %d, payload %d, " + "duration %d, addr %s\n", + ntohs(rtp->rtp_port), rtp->payload_type, + rtp->packet_duration_ms, inet_ntoa(rtp->addr)); return found_media; } diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 3591124..8768426 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -636,6 +636,10 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) * trunk.endpoints are set up properly. */ mgcp_free_endp(&endp); + /* There is no MGCP/SDP around, so set this by hand to make timestamp + * patching possible */ + rtp->packet_duration_ms = 20; + rtp->payload_type = 98; for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 24f9b33..f9dd7cb 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -16,22 +16,22 @@ Testing AUEP2 Testing MDCX1 Testing MDCX2 Testing CRCX -Packet duration not set +Detected packet duration: 40 Requested packetetization period: 20-20 Testing MDCX3 Packet duration not set Requested packetization period not set Testing MDCX4 -Packet duration not set +Detected packet duration: 40 Requested packetetization period: 20-20 Testing MDCX4_PT1 -Packet duration not set +Detected packet duration: 40 Requested packetetization period: 20-40 Testing MDCX4_PT2 -Packet duration not set +Detected packet duration: 40 Requested packetetization period: 20-20 Testing MDCX4_PT3 -Packet duration not set +Detected packet duration: 40 Requested packetization period not set Testing DLCX Detected packet duration: 20 -- 1.7.9.5 From jerlbeck at sysmocom.de Tue Dec 10 14:02:19 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Tue, 10 Dec 2013 15:02:19 +0100 Subject: [PATCH 5/5] mgcp: Optionally send ptime in SDP In-Reply-To: <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> References: <1386337174-11058-1-git-send-email-jerlbeck@sysmocom.de> <1386684139-8093-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386684139-8093-5-git-send-email-jerlbeck@sysmocom.de> Currently the SDP 'ptime' media attribute is never set in generated MGCP responses. This patch optionally includes the 'ptime' attribute if packet_duration_ms is != 0. This behaviour can be enabled/disabled by using the VTY command "sdp audio-payload send-ptime" (enabled by default). Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp.h | 1 + openbsc/src/libmgcp/mgcp_protocol.c | 23 +++++++++++++++- openbsc/src/libmgcp/mgcp_vty.c | 50 +++++++++++++++++++++++++++++++++++ openbsc/tests/mgcp/mgcp_test.c | 20 +++++++++++--- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 8ab52ce..0d64590 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -114,6 +114,7 @@ struct mgcp_trunk_config { char *audio_fmtp_extra; char *audio_name; int audio_payload; + int audio_send_ptime; int audio_loop; int omit_rtcp; diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index b9e1382..f632149 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -230,11 +230,12 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, const char *addr = endp->cfg->local_ip; const char *fmtp_extra = endp->bts_end.fmtp_extra; char sdp_record[4096]; + int len; if (!addr) addr = endp->cfg->source_addr; - snprintf(sdp_record, sizeof(sdp_record) - 1, + len = snprintf(sdp_record, sizeof(sdp_record) - 1, "I: %u\n\n" "v=0\r\n" "o=- %u 23 IN IP4 %s\r\n" @@ -247,7 +248,25 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, endp->net_end.local_port, endp->bts_end.payload_type, endp->bts_end.payload_type, endp->tcfg->audio_name, fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : ""); + + if (len < 0 || len >= sizeof(sdp_record)) + goto buffer_too_small; + + if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) { + int nchars = snprintf(sdp_record + len, sizeof(sdp_record) - len, + "a=ptime:%d\r\n", + endp->bts_end.packet_duration_ms); + if (nchars < 0 || nchars >= sizeof(sdp_record) - len) + goto buffer_too_small; + + len += nchars; + } return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); + +buffer_too_small: + LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %d (needed %d)\n", + sizeof(sdp_record), len); + return NULL; } /* @@ -1113,6 +1132,7 @@ struct mgcp_config *mgcp_config_alloc(void) cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL; cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000"); cfg->trunk.audio_payload = 126; + cfg->trunk.audio_send_ptime = 1; cfg->trunk.omit_rtcp = 0; INIT_LLIST_HEAD(&cfg->trunks); @@ -1135,6 +1155,7 @@ struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr) trunk->trunk_nr = nr; trunk->audio_name = talloc_strdup(cfg, "AMR/8000"); trunk->audio_payload = 126; + trunk->audio_send_ptime = 1; trunk->number_endpoints = 33; trunk->omit_rtcp = 0; llist_add_tail(&trunk->entry, &cfg->trunks); diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index e48b050..235b8bd 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -105,6 +105,8 @@ static int config_write_mgcp(struct vty *vty) if (g_cfg->trunk.audio_fmtp_extra) vty_out(vty, " sdp audio fmtp-extra %s%s", g_cfg->trunk.audio_fmtp_extra, VTY_NEWLINE); + vty_out(vty, " %ssdp audio-payload send-ptime%s", + g_cfg->trunk.audio_send_ptime ? "" : "no ", VTY_NEWLINE); vty_out(vty, " loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE); vty_out(vty, " number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE); if (g_cfg->call_agent_addr) @@ -391,6 +393,26 @@ ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd_old, "sdp audio payload name NAME", SDP_STR AUDIO_STR AUDIO_STR "Name\n" "Payload name\n") +DEFUN(cfg_mgcp_sdp_payload_send_ptime, + cfg_mgcp_sdp_payload_send_ptime_cmd, + "sdp audio-payload send-ptime", + SDP_STR AUDIO_STR + "Send SDP ptime (packet duration) attribute\n") +{ + g_cfg->trunk.audio_send_ptime = 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_no_sdp_payload_send_ptime, + cfg_mgcp_no_sdp_payload_send_ptime_cmd, + "no sdp audio-payload send-ptime", + NO_STR SDP_STR AUDIO_STR + "Send SDP ptime (packet duration) attribute\n") +{ + g_cfg->trunk.audio_send_ptime = 0; + return CMD_SUCCESS; +} + DEFUN(cfg_mgcp_loop, cfg_mgcp_loop_cmd, "loop (0|1)", @@ -568,6 +590,8 @@ static int config_write_trunk(struct vty *vty) trunk->audio_payload, VTY_NEWLINE); vty_out(vty, " sdp audio-payload name %s%s", trunk->audio_name, VTY_NEWLINE); + vty_out(vty, " %ssdp audio-payload send-ptime%s", + trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE); vty_out(vty, " loop %d%s", trunk->audio_loop, VTY_NEWLINE); if (trunk->omit_rtcp) @@ -649,6 +673,28 @@ DEFUN(cfg_trunk_loop, return CMD_SUCCESS; } +DEFUN(cfg_trunk_sdp_payload_send_ptime, + cfg_trunk_sdp_payload_send_ptime_cmd, + "sdp audio-payload send-ptime", + SDP_STR AUDIO_STR + "Send SDP ptime (packet duration) attribute\n") +{ + struct mgcp_trunk_config *trunk = vty->index; + trunk->audio_send_ptime = 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_no_sdp_payload_send_ptime, + cfg_trunk_no_sdp_payload_send_ptime_cmd, + "no sdp audio-payload send-ptime", + NO_STR SDP_STR AUDIO_STR + "Send SDP ptime (packet duration) attribute\n") +{ + struct mgcp_trunk_config *trunk = vty->index; + trunk->audio_send_ptime = 0; + return CMD_SUCCESS; +} + DEFUN(cfg_trunk_omit_rtcp, cfg_trunk_omit_rtcp_cmd, "rtcp-omit", @@ -966,6 +1012,8 @@ int mgcp_vty_init(void) install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd); + install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd); + install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd); install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); install_node(&trunk_node, config_write_trunk); @@ -983,6 +1031,8 @@ int mgcp_vty_init(void) install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd); install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd); + install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_ptime_cmd); + install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_ptime_cmd); return 0; } diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 8768426..2e2dfe7 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -81,7 +81,8 @@ static void test_strline(void) "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ - "a=rtpmap:126 AMR/8000\r\n" + "a=rtpmap:126 AMR/8000\r\n" \ + "a=ptime:20\r\n" #define MDCX4 "MDCX 18983216 1 at mgw MGCP 1.0\r\n" \ "C: 2\r\n" \ "I: 1\r\n" \ @@ -102,7 +103,8 @@ static void test_strline(void) "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ - "a=rtpmap:126 AMR/8000\r\n" + "a=rtpmap:126 AMR/8000\r\n" \ + "a=ptime:20\r\n" #define MDCX4_PT1 "MDCX 18983217 1 at mgw MGCP 1.0\r\n" \ "C: 2\r\n" \ @@ -168,7 +170,8 @@ static void test_strline(void) "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ - "a=rtpmap:126 AMR/8000\r\n" + "a=rtpmap:126 AMR/8000\r\n" \ + "a=ptime:20\r\n" #define CRCX_ZYN "CRCX 2 1 at mgw MGCP 1.0\r" \ "M: sendrecv\r" \ @@ -186,7 +189,8 @@ static void test_strline(void) "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ - "a=rtpmap:126 AMR/8000\r\n" + "a=rtpmap:126 AMR/8000\r\n" \ + "a=ptime:20\r\n" #define DLCX "DLCX 7 1 at mgw MGCP 1.0\r\n" \ "C: 2\r\n" @@ -290,6 +294,7 @@ static void test_messages(void) endp = &cfg->trunk.endpoints[i]; endp->net_end.payload_type = PTYPE_NONE; endp->net_end.packet_duration_ms = -1; + endp->bts_end.packet_duration_ms = 20; } for (i = 0; i < ARRAY_SIZE(tests); i++) { @@ -371,6 +376,13 @@ static void test_retransmission(void) mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); + /* reset endpoints */ + for (i = 0; i < cfg->trunk.number_endpoints; i++) { + struct mgcp_endpoint *endp; + endp = &cfg->trunk.endpoints[i]; + endp->bts_end.packet_duration_ms = 20; + } + for (i = 0; i < ARRAY_SIZE(retransmit); i++) { const struct mgcp_test *t = &retransmit[i]; struct msgb *inp; -- 1.7.9.5 From jolly at eversberg.eu Fri Dec 6 16:26:55 2013 From: jolly at eversberg.eu (Andreas Eversberg) Date: Fri, 6 Dec 2013 17:26:55 +0100 Subject: [PATCH] Add EFR support to TRAU muxer Message-ID: <1386347215-32522-1-git-send-email-jolly@eversberg.eu> Decoding and encoding of FR and EFR TRAU frames are put into seperate functions. CRC check is done to detect bad EFR TRAU frames. EFR support was tested with Nokia InSite BTS. --- openbsc/include/openbsc/mncc.h | 1 + openbsc/src/libmsc/gsm_04_08.c | 1 + openbsc/src/libtrau/trau_mux.c | 307 ++++++++++++++++++++++++++++++++-------- 3 files changed, 251 insertions(+), 58 deletions(-) diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h index 4a99bb1..ffc247b 100644 --- a/openbsc/include/openbsc/mncc.h +++ b/openbsc/include/openbsc/mncc.h @@ -95,6 +95,7 @@ struct gsm_call { #define GSM_TCHF_FRAME 0x0300 #define GSM_TCHF_FRAME_EFR 0x0301 +#define GSM_TCHF_BAD_FRAME 0x03ff #define MNCC_SOCKET_HELLO 0x0400 diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 7a92e4d..2df27a0 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -2910,6 +2910,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) case MNCC_FRAME_RECV: return tch_recv_mncc(net, data->callref, 1); case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: /* Find callref */ trans = trans_find_by_callref(net, data->callref); if (!trans) { diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c index d25e78f..f35fa67 100644 --- a/openbsc/src/libtrau/trau_mux.c +++ b/openbsc/src/libtrau/trau_mux.c @@ -31,6 +31,7 @@ #include #include #include +#include /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ @@ -47,6 +48,53 @@ static const uint8_t gsm_fr_map[] = { 3, 3, 3, 3 }; + +/* + * EFR TRAU parity + * + * g(x) = x^3 + x^1 + 1 + */ +const struct osmo_crc8gen_code gsm0860_efr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + +/* EFR parity bits */ +static inline void efr_parity_bits_1(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 0, 22); + memcpy(check_bits + 22 , d_bits + 24, 3); + check_bits[25] = d_bits[28]; +} + +static inline void efr_parity_bits_2(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 42, 10); + memcpy(check_bits + 10 , d_bits + 90, 2); +} + +static inline void efr_parity_bits_3(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 98, 5); + check_bits[5] = d_bits[104]; + memcpy(check_bits + 6 , d_bits + 143, 2); +} + +static inline void efr_parity_bits_4(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 151, 10); + memcpy(check_bits + 10 , d_bits + 199, 2); +} + +static inline void efr_parity_bits_5(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 207, 5); + check_bits[5] = d_bits[213]; + memcpy(check_bits + 6 , d_bits + 252, 2); +} + struct map_entry { struct llist_head list; struct gsm_e1_subslot src, dst; @@ -159,7 +207,117 @@ lookup_trau_upqueue(const struct gsm_e1_subslot *src) return NULL; } -static const uint8_t c_bits_check[] = { 0, 0, 0, 1, 0 }; +static const uint8_t c_bits_check_fr[] = { 0, 0, 0, 1, 0 }; +static const uint8_t c_bits_check_efr[] = { 1, 1, 0, 1, 0 }; + +static struct msgb *trau_decode_fr(struct upqueue_entry *ue, + const struct decoded_trau_frame *tf) +{ + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, k, l, o; + + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, + "GSM-DATA"); + if (!msg) + return NULL; + + 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; + msgb_put(msg, sizeof(struct gsm_data_frame) + 33); + + return msg; +} + +static struct msgb *trau_decode_efr(struct upqueue_entry *ue, + const struct decoded_trau_frame *tf) +{ + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, rc; + ubit_t check_bits[26]; + + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 31, + "GSM-DATA"); + if (!msg) + return NULL; + + frame = (struct gsm_data_frame *)msg->data; + memset(frame, 0, sizeof(struct gsm_data_frame)); + frame->msg_type = GSM_TCHF_FRAME_EFR; + frame->callref = ue->callref; + msgb_put(msg, sizeof(struct gsm_data_frame) + 31); + + if (tf->c_bits[11]) /* BFI */ + goto bad_frame; + + data = frame->data; + data[0] = 0xc << 4; + /* reassemble d-bits */ + for (i = 1, j = 4; i < 39; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_1(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 26, + tf->d_bits + 39); + if (rc) + goto bad_frame; + for (i = 42, j = 42; i < 95; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_2(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 95); + if (rc) + goto bad_frame; + for (i = 98, j = 95; i < 148; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_3(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 148); + if (rc) + goto bad_frame; + for (i = 151, j = 145; i < 204; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_4(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 204); + if (rc) + goto bad_frame; + for (i = 207, j = 198; i < 257; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_5(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 257); + if (rc) + goto bad_frame; + + return msg; + +bad_frame: + frame->msg_type = GSM_TCHF_BAD_FRAME; + + return msg; +} /* we get called by subchan_demux */ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, @@ -178,45 +336,25 @@ 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; + struct msgb *msg = NULL; /* frame shall be sent to upqueue */ if (!(ue = lookup_trau_upqueue(src_e1_ss))) return -EINVAL; if (!ue->callref) return -EINVAL; - if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check))) + if (!memcmp(tf.c_bits, c_bits_check_fr, 5)) + msg = trau_decode_fr(ue, &tf); + else if (!memcmp(tf.c_bits, c_bits_check_efr, 5)) + msg = trau_decode_efr(ue, &tf); + else { DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n", - osmo_hexdump(tf.c_bits, sizeof(c_bits_check))); - msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, - "GSM-DATA"); + osmo_hexdump(tf.c_bits, 5)); + DEBUGPC(DLMUX, "test trau (C1-C5) %s\n", + osmo_hexdump(c_bits_check_efr, 5)); + return -EINVAL; + } if (!msg) return -ENOMEM; - - 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; - msgb_put(msg, sizeof(struct gsm_data_frame) + 33); trau_tx_to_mncc(ue->net, msg); return 0; @@ -276,13 +414,86 @@ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref) return 0; } +static void trau_encode_fr(struct decoded_trau_frame *tf, + const unsigned char *data) +{ + int i, j, k, l, o; + + /* 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++; + } +} + +static void trau_encode_efr(struct decoded_trau_frame *tf, + const unsigned char *data) +{ + int i, j; + ubit_t check_bits[26]; + + /* set c-bits and t-bits */ + tf->c_bits[0] = 1; + tf->c_bits[1] = 1; + tf->c_bits[2] = 0; + tf->c_bits[3] = 1; + 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 */ + tf->d_bits[0] = 1; + for (i = 1, j = 4; i < 39; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_1(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 26, + tf->d_bits + 39); + for (i = 42, j = 42; i < 95; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_2(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 95); + for (i = 98, j = 95; i < 148; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_3(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 148); + for (i = 151, j = 145; i < 204; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_4(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 204); + for (i = 207, j = 198; i < 257; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_5(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 257); +} + int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) { uint8_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); @@ -291,30 +502,10 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) 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++; - } + trau_encode_fr(&tf, frame->data); + break; + case GSM_TCHF_FRAME_EFR: + trau_encode_efr(&tf, frame->data); break; default: DEBUGPC(DLMUX, "unsupported message type %d\n", -- 1.7.3.4 From laforge at gnumonks.org Sat Dec 7 17:01:50 2013 From: laforge at gnumonks.org (Harald Welte) Date: Sat, 7 Dec 2013 18:01:50 +0100 Subject: [PATCH] Add EFR support to TRAU muxer In-Reply-To: <1386347215-32522-1-git-send-email-jolly@eversberg.eu> References: <1386347215-32522-1-git-send-email-jolly@eversberg.eu> Message-ID: <20131207170150.GZ8435@nataraja.gnumonks.org> On Fri, Dec 06, 2013 at 05:26:55PM +0100, Andreas Eversberg wrote: > Decoding and encoding of FR and EFR TRAU frames are put into seperate > functions. CRC check is done to detect bad EFR TRAU frames. Looks fine to me. > EFR support was tested with Nokia InSite BTS. Has somebody already tested with a BS-11 / Ericsson in FR mode? Just to make sure we have no regressions... If somebody on the list can confirm that it still works with FR/BS-11, I would merge your patch. Thanks! -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From andreas at eversberg.eu Sun Dec 8 07:14:21 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Sun, 08 Dec 2013 08:14:21 +0100 Subject: [PATCH] Add EFR support to TRAU muxer In-Reply-To: <20131207170150.GZ8435@nataraja.gnumonks.org> References: <1386347215-32522-1-git-send-email-jolly@eversberg.eu> <20131207170150.GZ8435@nataraja.gnumonks.org> Message-ID: <52A41C4D.7030802@eversberg.eu> > >> EFR support was tested with Nokia InSite BTS. >> > Has somebody already tested with a BS-11 / Ericsson in FR mode? Just > to make sure we have no regressions... > just tested it with BS11. both FR and EFR work via MNCC socket and built-in call control. From holger at freyther.de Sun Dec 8 09:57:28 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Sun, 8 Dec 2013 10:57:28 +0100 Subject: [PATCH] Add EFR support to TRAU muxer In-Reply-To: <52A41C4D.7030802@eversberg.eu> References: <1386347215-32522-1-git-send-email-jolly@eversberg.eu> <20131207170150.GZ8435@nataraja.gnumonks.org> <52A41C4D.7030802@eversberg.eu> Message-ID: <20131208095728.GM7478@xiaoyu.lan> On Sun, Dec 08, 2013 at 08:14:21AM +0100, Andreas Eversberg wrote: > just tested it with BS11. both FR and EFR work via MNCC socket and > built-in call control. > Good Morning, given that we have some issues with encode/decode of the RBB in the osmo-pcu, I think it would be nice to have a simple testcase that encodes/decodes an idle EFR frame here to make sure that we are at least compatible to ourselves. kind regards holger From jolly at eversberg.eu Sat Dec 7 17:32:28 2013 From: jolly at eversberg.eu (Andreas Eversberg) Date: Sat, 7 Dec 2013 18:32:28 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) Message-ID: <1386437548-2280-1-git-send-email-jolly@eversberg.eu> Speech codings which are not supported by BTS will be removed from the bearer capability information element after parsing. This way it is not required for the MNCC application to consider support of each BTS. Only GSM full rate is supported by default. --- openbsc/include/openbsc/gsm_data_shared.h | 9 ++++ openbsc/src/libbsc/bsc_vty.c | 72 +++++++++++++++++++++++++++++ openbsc/src/libmsc/gsm_04_08.c | 28 +++++++++++ 3 files changed, 109 insertions(+), 0 deletions(-) diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h index 84d15ef..4a41498 100644 --- a/openbsc/include/openbsc/gsm_data_shared.h +++ b/openbsc/include/openbsc/gsm_data_shared.h @@ -143,6 +143,12 @@ struct bts_ul_meas { uint8_t inv_rssi; }; +struct bts_codec_conf { + uint8_t hr; + uint8_t efr; + uint8_t amr; +}; + struct amr_mode { uint8_t mode; uint8_t threshold; @@ -704,6 +710,9 @@ struct gsm_bts { /* exclude the BTS from the global RF Lock handling */ int excl_from_rf_lock; + + /* supported codecs beside FR */ + struct bts_codec_conf codec; #endif /* ROLE_BSC */ void *role; }; diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index b034981..f444427 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -629,6 +629,15 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) } } + vty_out(vty, " codec-support fr"); + if (bts->codec.hr) + vty_out(vty, " hr"); + if (bts->codec.efr) + vty_out(vty, " efr"); + if (bts->codec.amr) + vty_out(vty, " amr"); + vty_out(vty, "%s", VTY_NEWLINE); + config_write_bts_gprs(vty, bts); if (bts->excl_from_rf_lock) @@ -2635,6 +2644,65 @@ DEFUN(cfg_bts_no_excl_rf_lock, return CMD_SUCCESS; } +static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[]) +{ + struct gsm_bts *bts = vty->index; + struct bts_codec_conf *codec = &bts->codec; + int i; + + codec->hr = 0; + codec->efr = 0; + codec->amr = 0; + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "hr")) + codec->hr = 1; + if (!strcmp(argv[i], "efr")) + codec->efr = 1; + if (!strcmp(argv[i], "amr")) + codec->amr = 1; + } +} + +#define CODEC_PAR_STR " (fr|hr|efr|amr)" +#define CODEC_HELP_STR "Full Rate (mandatory)\nHalf Rate\n" \ + "Enhanced Full Rate\nAdaptive Multirate\n" + +DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, + "codec-support" CODEC_PAR_STR, + "Codec Support settings\n" + CODEC_HELP_STR) +{ + _get_codec_from_arg(vty, 1, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, + "codec-support" CODEC_PAR_STR CODEC_PAR_STR, + "Codec Support settings\n" + CODEC_HELP_STR CODEC_HELP_STR) +{ + _get_codec_from_arg(vty, 2, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, + "codec-support" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, + "Codec Support settings\n" + CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) +{ + _get_codec_from_arg(vty, 3, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd, + "codec-support" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, + "Codec Support settings\n" + CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) +{ + _get_codec_from_arg(vty, 4, argv); + return CMD_SUCCESS; +} + #define TRX_TEXT "Radio Transceiver\n" /* per TRX configuration */ @@ -3231,6 +3299,10 @@ int bsc_vty_init(const struct log_info *cat) install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd); install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd); + install_element(BTS_NODE, &cfg_bts_codec1_cmd); + install_element(BTS_NODE, &cfg_bts_codec2_cmd); + install_element(BTS_NODE, &cfg_bts_codec3_cmd); + install_element(BTS_NODE, &cfg_bts_codec4_cmd); install_element(BTS_NODE, &cfg_trx_cmd); install_node(&trx_node, dummy_config_write); diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 3cfc455..dec97f2 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -79,6 +79,29 @@ struct gsm_lai { uint16_t lac; }; +static int apply_codec_restrictions(struct gsm_bts *bts, + struct gsm_mncc_bearer_cap *bcap) +{ + int i, j; + + /* remove unsupported speech versions from list */ + for (i = 0, j = 0; bcap->speech_ver[i] >= 0; i++) { + if (bcap->speech_ver[i] == GSM48_BCAP_SV_FR) + bcap->speech_ver[j++] = GSM48_BCAP_SV_FR; + if (bcap->speech_ver[i] == GSM48_BCAP_SV_EFR && bts->codec.efr) + bcap->speech_ver[j++] = GSM48_BCAP_SV_EFR; + if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_F && bts->codec.amr) + bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_F; + if (bcap->speech_ver[i] == GSM48_BCAP_SV_HR && bts->codec.hr) + bcap->speech_ver[j++] = GSM48_BCAP_SV_HR; + if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_H && bts->codec.amr) + bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_H; + } + bcap->speech_ver[j] = -1; + + return 0; +} + static uint32_t new_callref = 0x80000001; void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg) @@ -1763,6 +1786,7 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) setup.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&setup.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &setup.bearer_cap); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { @@ -1916,6 +1940,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) call_conf.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&call_conf.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &call_conf.bearer_cap); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { @@ -2604,6 +2629,7 @@ static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); @@ -2646,6 +2672,7 @@ static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } new_cc_state(trans, GSM_CSTATE_ACTIVE); @@ -2686,6 +2713,7 @@ static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) modify.fields |= GSM48_IE_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { -- 1.7.3.4 From holger at freyther.de Wed Dec 18 15:21:25 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Wed, 18 Dec 2013 16:21:25 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) In-Reply-To: <1386437548-2280-1-git-send-email-jolly@eversberg.eu> References: <1386437548-2280-1-git-send-email-jolly@eversberg.eu> Message-ID: <20131218152125.GB13933@xiaoyu.lan> On Sat, Dec 07, 2013 at 06:32:28PM +0100, Andreas Eversberg wrote: Hi! > openbsc/src/libbsc/bsc_vty.c | 72 +++++++++++++++++++++++++++++ I just wanted to merge the code but > + vty_out(vty, " codec-support fr"); so your code assumes that FR is supported anyway. > +#define CODEC_PAR_STR " (fr|hr|efr|amr)" What is the point of being allowed to not configure it? From andreas at eversberg.eu Wed Dec 18 16:17:16 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Wed, 18 Dec 2013 17:17:16 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) In-Reply-To: <20131218152125.GB13933@xiaoyu.lan> References: <1386437548-2280-1-git-send-email-jolly@eversberg.eu> <20131218152125.GB13933@xiaoyu.lan> Message-ID: <52B1CA8C.2040802@eversberg.eu> Holger Hans Peter Freyther wrote: > so your code assumes that FR is supported anyway. > >> > +#define CODEC_PAR_STR " (fr|hr|efr|amr)" > What is the point of being allowed to not configure it? i just wanted to show in the vty config that fr is supported, even if it is mandatory and cannot be made unsupported. From holger at freyther.de Thu Dec 19 10:09:01 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 19 Dec 2013 11:09:01 +0100 Subject: [PATCH 1/4] Each BTS can be configured for speech support (other than GSM full rate) In-Reply-To: <52B1CA8C.2040802@eversberg.eu> References: <1386437548-2280-1-git-send-email-jolly@eversberg.eu> <20131218152125.GB13933@xiaoyu.lan> <52B1CA8C.2040802@eversberg.eu> Message-ID: <20131219100901.GC30292@xiaoyu.lan> On Wed, Dec 18, 2013 at 05:17:16PM +0100, Andreas Eversberg wrote: > i just wanted to show in the vty config that fr is supported, even > if it is mandatory and cannot be made unsupported. What about this? It makes it mandatory to define fr first. The other things I noticed is: * Currently the default codec MNCC-intern uses is EFR. After the change it will be FR. * The BTS should be involved as well to ack/nack the codec support (we can do that later) * The BSC part should start to honor this support too. diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index 1bc0ea1..9801b6a 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -2663,13 +2663,13 @@ static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[]) } } -#define CODEC_PAR_STR " (fr|hr|efr|amr)" -#define CODEC_HELP_STR "Full Rate (mandatory)\nHalf Rate\n" \ +#define CODEC_PAR_STR " (hr|efr|amr)" +#define CODEC_HELP_STR "Half Rate\n" \ "Enhanced Full Rate\nAdaptive Multirate\n" DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, - "codec-support" CODEC_PAR_STR, - "Codec Support settings\n" + "codec-support fr" CODEC_PAR_STR, + "Codec Support settings\nFullrate\n" CODEC_HELP_STR) { _get_codec_from_arg(vty, 1, argv); @@ -2677,8 +2677,8 @@ DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, } DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, - "codec-support" CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\n" + "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR, + "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 2, argv); @@ -2686,8 +2686,8 @@ DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, } DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, - "codec-support" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\n" + "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, + "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 3, argv); @@ -2695,8 +2695,8 @@ DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, } DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd, - "codec-support" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\n" + "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, + "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 4, argv); From andreas at eversberg.eu Sun Dec 8 16:21:02 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Sun, 08 Dec 2013 17:21:02 +0100 Subject: Bug in range 512 encoding Message-ID: <52A49C6E.7090300@eversberg.eu> diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c index 02a75a5..45a49da 100644 --- a/openbsc/src/libbsc/arfcn_range_encode.c +++ b/openbsc/src/libbsc/arfcn_range_encode.c @@ -213,7 +213,7 @@ int range_enc_range512(uint8_t *chan_list, int f0, int *w) write_orig_arfcn(chan_list, f0); range512 = (struct gsm48_range_512 *) &chan_list[0]; - range512->form_id = chan_list[0] = 0x44; + range512->form_id = 0x44; /* W(1) */ range512->w1_hi = HIGH_BITS(w, 1, 9, 7); hi, just found a bug in range 512 channel list encoding. the useless write of 0x44 to chan_list[0] will destroy the LSB, which is part of frequency 0, previously written by write_orig_arfcn(). after fixing this, a frequency of ARFCN 512 encodes correctly. regards, andreas From holger at freyther.de Sun Dec 8 18:49:48 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Sun, 8 Dec 2013 19:49:48 +0100 Subject: Bug in range 512 encoding In-Reply-To: <52A49C6E.7090300@eversberg.eu> References: <52A49C6E.7090300@eversberg.eu> Message-ID: <20131208184948.GS7478@xiaoyu.lan> On Sun, Dec 08, 2013 at 05:21:02PM +0100, Andreas Eversberg wrote: > just found a bug in range 512 channel list encoding. the useless write of 0x44 to chan_list[0] will destroy the LSB, which is part of frequency 0, previously written by write_orig_arfcn(). after fixing this, a frequency of ARFCN 512 encodes correctly. o> What about the below snippet. Does it work for you? It matches what the other (not implemented) routines do. Which ARFCN list did you encode? In our tests we probably never had orig_arfcn_hi:1 set. diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c index 02a75a5..5a2345e 100644 --- a/openbsc/src/libbsc/arfcn_range_encode.c +++ b/openbsc/src/libbsc/arfcn_range_encode.c @@ -210,10 +210,11 @@ int range_enc_range256(uint8_t *chan_list, int f0, int *w) int range_enc_range512(uint8_t *chan_list, int f0, int *w) { struct gsm48_range_512 *range512; + + chan_list[0] = 0x44; write_orig_arfcn(chan_list, f0); range512 = (struct gsm48_range_512 *) &chan_list[0]; - range512->form_id = chan_list[0] = 0x44; /* W(1) */ range512->w1_hi = HIGH_BITS(w, 1, 9, 7); From andreas at eversberg.eu Sun Dec 8 18:57:44 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Sun, 08 Dec 2013 19:57:44 +0100 Subject: Bug in range 512 encoding In-Reply-To: <20131208184948.GS7478@xiaoyu.lan> References: <52A49C6E.7090300@eversberg.eu> <20131208184948.GS7478@xiaoyu.lan> Message-ID: <52A4C128.4030607@eversberg.eu> Holger Hans Peter Freyther wrote: > What about the below snippet. Does it work for you? It matches what the > other (not implemented) routines do. Which ARFCN list did you encode? In > our tests we probably never had orig_arfcn_hi:1 set. > > diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c > index 02a75a5..5a2345e 100644 > --- a/openbsc/src/libbsc/arfcn_range_encode.c > +++ b/openbsc/src/libbsc/arfcn_range_encode.c > @@ -210,10 +210,11 @@ int range_enc_range256(uint8_t *chan_list, int f0, int *w) > int range_enc_range512(uint8_t *chan_list, int f0, int *w) > { > struct gsm48_range_512 *range512; > + > + chan_list[0] = 0x44; > write_orig_arfcn(chan_list, f0); > > range512 = (struct gsm48_range_512 *)&chan_list[0]; > - range512->form_id = chan_list[0] = 0x44; > > /* W(1) */ > range512->w1_hi = HIGH_BITS(w, 1, 9, 7); "range512->form_id" points to the upper 7 bits of chan_list[0]. when you do that, you would write 0x44 to chan_list[0] instead of the required 0x88. use "chan_list[0] = 0x88" instead. From holger at freyther.de Sun Dec 8 19:16:00 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Sun, 8 Dec 2013 20:16:00 +0100 Subject: Bug in range 512 encoding In-Reply-To: <52A4C128.4030607@eversberg.eu> References: <52A49C6E.7090300@eversberg.eu> <20131208184948.GS7478@xiaoyu.lan> <52A4C128.4030607@eversberg.eu> Message-ID: <20131208191600.GT7478@xiaoyu.lan> On Sun, Dec 08, 2013 at 07:57:44PM +0100, Andreas Eversberg wrote: > "range512->form_id" points to the upper 7 bits of chan_list[0]. when > you do that, you would write 0x44 to chan_list[0] instead of the > required 0x88. use "chan_list[0] = 0x88" instead. thanks, so diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c index 02a75a5..8ed4711 100644 --- a/openbsc/src/libbsc/arfcn_range_encode.c +++ b/openbsc/src/libbsc/arfcn_range_encode.c @@ -210,10 +210,11 @@ int range_enc_range256(uint8_t *chan_list, int f0, int *w) int range_enc_range512(uint8_t *chan_list, int f0, int *w) { struct gsm48_range_512 *range512; + + chan_list[0] = 0x88; write_orig_arfcn(chan_list, f0); range512 = (struct gsm48_range_512 *) &chan_list[0]; - range512->form_id = chan_list[0] = 0x44; /* W(1) */ range512->w1_hi = HIGH_BITS(w, 1, 9, 7); will do the job. I will create a testcase and then include a patch. From jolly at eversberg.eu Mon Dec 9 07:45:24 2013 From: jolly at eversberg.eu (Andreas Eversberg) Date: Mon, 9 Dec 2013 08:45:24 +0100 Subject: [PATCH] Add EFR support to TRAU muxer + test case Message-ID: <1386575124-28659-1-git-send-email-jolly@eversberg.eu> Decoding and encoding of FR and EFR TRAU frames are put into seperate functions. CRC check is done to detect bad EFR TRAU frames. The test case includes FR and EFR transcoding. EFR support was tested with Nokia InSite BTS and Siemens BS11. --- openbsc/configure.ac | 1 + openbsc/include/openbsc/mncc.h | 1 + openbsc/include/openbsc/trau_mux.h | 10 ++ openbsc/src/libmsc/gsm_04_08.c | 1 + openbsc/src/libtrau/trau_mux.c | 307 +++++++++++++++++++++++++++++------- openbsc/tests/Makefile.am | 2 +- openbsc/tests/testsuite.at | 6 + openbsc/tests/trau/Makefile.am | 17 ++ openbsc/tests/trau/trau_test.c | 82 ++++++++++ 9 files changed, 368 insertions(+), 59 deletions(-) create mode 100644 openbsc/tests/trau/Makefile.am create mode 100644 openbsc/tests/trau/trau_test.c diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 081e416..fe99569 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -169,6 +169,7 @@ AC_OUTPUT( tests/si/Makefile tests/abis/Makefile tests/smpp/Makefile + tests/trau/Makefile doc/Makefile doc/examples/Makefile Makefile) diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h index 4a99bb1..ffc247b 100644 --- a/openbsc/include/openbsc/mncc.h +++ b/openbsc/include/openbsc/mncc.h @@ -95,6 +95,7 @@ struct gsm_call { #define GSM_TCHF_FRAME 0x0300 #define GSM_TCHF_FRAME_EFR 0x0301 +#define GSM_TCHF_BAD_FRAME 0x03ff #define MNCC_SOCKET_HELLO 0x0400 diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h index 667f623..d211d8d 100644 --- a/openbsc/include/openbsc/trau_mux.h +++ b/openbsc/include/openbsc/trau_mux.h @@ -56,3 +56,13 @@ int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan); /* callback invoked if we receive TRAU frames */ int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); + +/* TRAU frame transcoding */ +struct msgb *trau_decode_fr(uint32_t callref, + const struct decoded_trau_frame *tf); +struct msgb *trau_decode_efr(uint32_t callref, + const struct decoded_trau_frame *tf); +void trau_encode_fr(struct decoded_trau_frame *tf, + const unsigned char *data); +void trau_encode_efr(struct decoded_trau_frame *tf, + const unsigned char *data); diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 7a92e4d..2df27a0 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -2910,6 +2910,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) case MNCC_FRAME_RECV: return tch_recv_mncc(net, data->callref, 1); case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: /* Find callref */ trans = trans_find_by_callref(net, data->callref); if (!trans) { diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c index d25e78f..c73884b 100644 --- a/openbsc/src/libtrau/trau_mux.c +++ b/openbsc/src/libtrau/trau_mux.c @@ -31,6 +31,7 @@ #include #include #include +#include /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ @@ -47,6 +48,53 @@ static const uint8_t gsm_fr_map[] = { 3, 3, 3, 3 }; + +/* + * EFR TRAU parity + * + * g(x) = x^3 + x^1 + 1 + */ +const struct osmo_crc8gen_code gsm0860_efr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + +/* EFR parity bits */ +static inline void efr_parity_bits_1(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 0, 22); + memcpy(check_bits + 22 , d_bits + 24, 3); + check_bits[25] = d_bits[28]; +} + +static inline void efr_parity_bits_2(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 42, 10); + memcpy(check_bits + 10 , d_bits + 90, 2); +} + +static inline void efr_parity_bits_3(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 98, 5); + check_bits[5] = d_bits[104]; + memcpy(check_bits + 6 , d_bits + 143, 2); +} + +static inline void efr_parity_bits_4(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 151, 10); + memcpy(check_bits + 10 , d_bits + 199, 2); +} + +static inline void efr_parity_bits_5(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 207, 5); + check_bits[5] = d_bits[213]; + memcpy(check_bits + 6 , d_bits + 252, 2); +} + struct map_entry { struct llist_head list; struct gsm_e1_subslot src, dst; @@ -159,7 +207,117 @@ lookup_trau_upqueue(const struct gsm_e1_subslot *src) return NULL; } -static const uint8_t c_bits_check[] = { 0, 0, 0, 1, 0 }; +static const uint8_t c_bits_check_fr[] = { 0, 0, 0, 1, 0 }; +static const uint8_t c_bits_check_efr[] = { 1, 1, 0, 1, 0 }; + +struct msgb *trau_decode_fr(uint32_t callref, + const struct decoded_trau_frame *tf) +{ + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, k, l, o; + + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, + "GSM-DATA"); + if (!msg) + return NULL; + + 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 = callref; + msgb_put(msg, sizeof(struct gsm_data_frame) + 33); + + return msg; +} + +struct msgb *trau_decode_efr(uint32_t callref, + const struct decoded_trau_frame *tf) +{ + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, rc; + ubit_t check_bits[26]; + + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 31, + "GSM-DATA"); + if (!msg) + return NULL; + + frame = (struct gsm_data_frame *)msg->data; + memset(frame, 0, sizeof(struct gsm_data_frame)); + frame->msg_type = GSM_TCHF_FRAME_EFR; + frame->callref = callref; + msgb_put(msg, sizeof(struct gsm_data_frame) + 31); + + if (tf->c_bits[11]) /* BFI */ + goto bad_frame; + + data = frame->data; + data[0] = 0xc << 4; + /* reassemble d-bits */ + for (i = 1, j = 4; i < 39; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_1(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 26, + tf->d_bits + 39); + if (rc) + goto bad_frame; + for (i = 42, j = 42; i < 95; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_2(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 95); + if (rc) + goto bad_frame; + for (i = 98, j = 95; i < 148; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_3(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 148); + if (rc) + goto bad_frame; + for (i = 151, j = 145; i < 204; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_4(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 204); + if (rc) + goto bad_frame; + for (i = 207, j = 198; i < 257; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_5(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 257); + if (rc) + goto bad_frame; + + return msg; + +bad_frame: + frame->msg_type = GSM_TCHF_BAD_FRAME; + + return msg; +} /* we get called by subchan_demux */ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, @@ -178,45 +336,25 @@ 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; + struct msgb *msg = NULL; /* frame shall be sent to upqueue */ if (!(ue = lookup_trau_upqueue(src_e1_ss))) return -EINVAL; if (!ue->callref) return -EINVAL; - if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check))) + if (!memcmp(tf.c_bits, c_bits_check_fr, 5)) + msg = trau_decode_fr(ue->callref, &tf); + else if (!memcmp(tf.c_bits, c_bits_check_efr, 5)) + msg = trau_decode_efr(ue->callref, &tf); + else { DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n", - osmo_hexdump(tf.c_bits, sizeof(c_bits_check))); - msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, - "GSM-DATA"); + osmo_hexdump(tf.c_bits, 5)); + DEBUGPC(DLMUX, "test trau (C1-C5) %s\n", + osmo_hexdump(c_bits_check_efr, 5)); + return -EINVAL; + } if (!msg) return -ENOMEM; - - 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; - msgb_put(msg, sizeof(struct gsm_data_frame) + 33); trau_tx_to_mncc(ue->net, msg); return 0; @@ -276,13 +414,86 @@ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref) return 0; } +void trau_encode_fr(struct decoded_trau_frame *tf, + const unsigned char *data) +{ + int i, j, k, l, o; + + /* 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++; + } +} + +void trau_encode_efr(struct decoded_trau_frame *tf, + const unsigned char *data) +{ + int i, j; + ubit_t check_bits[26]; + + /* set c-bits and t-bits */ + tf->c_bits[0] = 1; + tf->c_bits[1] = 1; + tf->c_bits[2] = 0; + tf->c_bits[3] = 1; + 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 */ + tf->d_bits[0] = 1; + for (i = 1, j = 4; i < 39; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_1(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 26, + tf->d_bits + 39); + for (i = 42, j = 42; i < 95; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_2(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 95); + for (i = 98, j = 95; i < 148; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_3(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 148); + for (i = 151, j = 145; i < 204; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_4(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 204); + for (i = 207, j = 198; i < 257; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_5(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 257); +} + int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) { uint8_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); @@ -291,30 +502,10 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) 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++; - } + trau_encode_fr(&tf, frame->data); + break; + case GSM_TCHF_FRAME_EFR: + trau_encode_efr(&tf, frame->data); break; default: DEBUGPC(DLMUX, "unsupported message type %d\n", diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index 7f51bfa..d4bb954 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = gsm0408 db channel mgcp gprs si abis gbproxy +SUBDIRS = gsm0408 db channel mgcp gprs si abis gbproxy trau if BUILD_NAT SUBDIRS += bsc-nat bsc-nat-trie diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 76110f5..652cfe9 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -81,3 +81,9 @@ AT_KEYWORDS([gbproxy]) cat $abs_srcdir/gbproxy/gbproxy_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gbproxy/gbproxy_test], [], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([trau]) +AT_KEYWORDS([trau]) +cat $abs_srcdir/trau/trau_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/trau/trau_test], [], [expout], [ignore]) +AT_CLEANUP diff --git a/openbsc/tests/trau/Makefile.am b/openbsc/tests/trau/Makefile.am new file mode 100644 index 0000000..d4aa1c3 --- /dev/null +++ b/openbsc/tests/trau/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = trau_test.ok + +noinst_PROGRAMS = trau_test + +trau_test_SOURCES = trau_test.c +trau_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOGSM_LIBS) $(LIBSMPP34_LIBS) $(LIBOSMOVTY_LIBS) -ldl -ldbi + diff --git a/openbsc/tests/trau/trau_test.c b/openbsc/tests/trau/trau_test.c new file mode 100644 index 0000000..f8a48db --- /dev/null +++ b/openbsc/tests/trau/trau_test.c @@ -0,0 +1,82 @@ +/* (C) 2013 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include + +void test_trau_fr_efr(unsigned char *data) +{ + struct decoded_trau_frame tf; + struct msgb *msg; + struct gsm_data_frame *frame; + + printf("Testing TRAU FR transcoding.\n"); + data[0] = 0xd0; + trau_encode_fr(&tf, data); + tf.c_bits[11] = 0; /* clear BFI */ + msg = trau_decode_fr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_FRAME); + OSMO_ASSERT(!memcmp(frame->data, data, 33)); + msgb_free(msg); + + printf("Testing TRAU EFR transcoding.\n"); + data[0] = 0xc0; + trau_encode_efr(&tf, data); + OSMO_ASSERT(tf.d_bits[0] == 1); /* spare bit must be 1 */ + tf.c_bits[11] = 0; /* clear BFI */ + msg = trau_decode_efr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_FRAME_EFR); + OSMO_ASSERT(!memcmp(frame->data, data, 31)); + + printf("Testing TRAU EFR decoding with CRC error.\n"); + tf.d_bits[0] = 0; /* spare bit must be included */ + msg = trau_decode_efr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_BAD_FRAME); + msgb_free(msg); +} + +int main() +{ + unsigned char data[33]; + int i; + + memset(data, 0x00, sizeof(data)); + test_trau_fr_efr(data); + memset(data, 0xff, sizeof(data)); + test_trau_fr_efr(data); + srandom(42); + for (i = 0; i < sizeof(data); i++) + data[i] = random(); + test_trau_fr_efr(data); + printf("Done\n"); + return 0; +} + +/* stubs */ +void vty_out() {} -- 1.7.3.4 From holger at freyther.de Wed Dec 18 15:23:03 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Wed, 18 Dec 2013 16:23:03 +0100 Subject: [PATCH] Add EFR support to TRAU muxer + test case In-Reply-To: <1386575124-28659-1-git-send-email-jolly@eversberg.eu> References: <1386575124-28659-1-git-send-email-jolly@eversberg.eu> Message-ID: <20131218152303.GC13933@xiaoyu.lan> On Mon, Dec 09, 2013 at 08:45:24AM +0100, Andreas Eversberg wrote: Good Afternoon, > Decoding and encoding of FR and EFR TRAU frames are put into seperate > functions. CRC check is done to detect bad EFR TRAU frames. this patch does not apply against master. Could you please rebase and re-send? From andreas at eversberg.eu Thu Dec 19 08:20:17 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Thu, 19 Dec 2013 09:20:17 +0100 Subject: [PATCH] Add EFR support to TRAU muxer + test case In-Reply-To: <20131218152303.GC13933@xiaoyu.lan> References: <1386575124-28659-1-git-send-email-jolly@eversberg.eu> <20131218152303.GC13933@xiaoyu.lan> Message-ID: <52B2AC41.70005@eversberg.eu> Holger Hans Peter Freyther wrote: > this patch does not apply against master. Could you please rebase > and re-send? > ok, i fixed it. see attachment. -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: 0001-Add-EFR-support-to-TRAU-muxer-test-case.patch URL: From jolly at eversberg.eu Fri Dec 6 15:59:10 2013 From: jolly at eversberg.eu (Andreas Eversberg) Date: Fri, 6 Dec 2013 16:59:10 +0100 Subject: [PATCH] Add EFR support to TRAU muxer + test case Message-ID: Decoding and encoding of FR and EFR TRAU frames are put into seperate functions. CRC check is done to detect bad EFR TRAU frames. The test case includes FR and EFR transcoding. EFR support was tested with Nokia InSite BTS and Siemens BS11. --- openbsc/configure.ac | 1 + openbsc/include/openbsc/mncc.h | 1 + openbsc/include/openbsc/trau_mux.h | 10 ++ openbsc/src/libmsc/gsm_04_08.c | 1 + openbsc/src/libtrau/trau_mux.c | 307 +++++++++++++++++++++++++++++------- openbsc/tests/Makefile.am | 2 +- openbsc/tests/testsuite.at | 6 + openbsc/tests/trau/Makefile.am | 17 ++ openbsc/tests/trau/trau_test.c | 82 ++++++++++ 9 files changed, 368 insertions(+), 59 deletions(-) create mode 100644 openbsc/tests/trau/Makefile.am create mode 100644 openbsc/tests/trau/trau_test.c diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 081e416..fe99569 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -169,6 +169,7 @@ AC_OUTPUT( tests/si/Makefile tests/abis/Makefile tests/smpp/Makefile + tests/trau/Makefile doc/Makefile doc/examples/Makefile Makefile) diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h index 4a99bb1..ffc247b 100644 --- a/openbsc/include/openbsc/mncc.h +++ b/openbsc/include/openbsc/mncc.h @@ -95,6 +95,7 @@ struct gsm_call { #define GSM_TCHF_FRAME 0x0300 #define GSM_TCHF_FRAME_EFR 0x0301 +#define GSM_TCHF_BAD_FRAME 0x03ff #define MNCC_SOCKET_HELLO 0x0400 diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h index 2c01b06..3de50f7 100644 --- a/openbsc/include/openbsc/trau_mux.h +++ b/openbsc/include/openbsc/trau_mux.h @@ -53,3 +53,13 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame); /* callback invoked if we receive TRAU frames */ int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); + +/* TRAU frame transcoding */ +struct msgb *trau_decode_fr(uint32_t callref, + const struct decoded_trau_frame *tf); +struct msgb *trau_decode_efr(uint32_t callref, + const struct decoded_trau_frame *tf); +void trau_encode_fr(struct decoded_trau_frame *tf, + const unsigned char *data); +void trau_encode_efr(struct decoded_trau_frame *tf, + const unsigned char *data); diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 3cfc455..6360393 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -2876,6 +2876,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) case MNCC_FRAME_RECV: return tch_recv_mncc(net, data->callref, 1); case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: /* Find callref */ trans = trans_find_by_callref(net, data->callref); if (!trans) { diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c index 9272ac0..727c586 100644 --- a/openbsc/src/libtrau/trau_mux.c +++ b/openbsc/src/libtrau/trau_mux.c @@ -30,6 +30,7 @@ #include #include #include +#include /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ @@ -46,6 +47,53 @@ static const uint8_t gsm_fr_map[] = { 3, 3, 3, 3 }; + +/* + * EFR TRAU parity + * + * g(x) = x^3 + x^1 + 1 + */ +const struct osmo_crc8gen_code gsm0860_efr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + +/* EFR parity bits */ +static inline void efr_parity_bits_1(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 0, 22); + memcpy(check_bits + 22 , d_bits + 24, 3); + check_bits[25] = d_bits[28]; +} + +static inline void efr_parity_bits_2(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 42, 10); + memcpy(check_bits + 10 , d_bits + 90, 2); +} + +static inline void efr_parity_bits_3(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 98, 5); + check_bits[5] = d_bits[104]; + memcpy(check_bits + 6 , d_bits + 143, 2); +} + +static inline void efr_parity_bits_4(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 151, 10); + memcpy(check_bits + 10 , d_bits + 199, 2); +} + +static inline void efr_parity_bits_5(ubit_t *check_bits, const ubit_t *d_bits) +{ + memcpy(check_bits + 0 , d_bits + 207, 5); + check_bits[5] = d_bits[213]; + memcpy(check_bits + 6 , d_bits + 252, 2); +} + struct map_entry { struct llist_head list; struct gsm_e1_subslot src, dst; @@ -158,7 +206,117 @@ lookup_trau_upqueue(const struct gsm_e1_subslot *src) return NULL; } -static const uint8_t c_bits_check[] = { 0, 0, 0, 1, 0 }; +static const uint8_t c_bits_check_fr[] = { 0, 0, 0, 1, 0 }; +static const uint8_t c_bits_check_efr[] = { 1, 1, 0, 1, 0 }; + +struct msgb *trau_decode_fr(uint32_t callref, + const struct decoded_trau_frame *tf) +{ + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, k, l, o; + + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, + "GSM-DATA"); + if (!msg) + return NULL; + + 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 = callref; + msgb_put(msg, sizeof(struct gsm_data_frame) + 33); + + return msg; +} + +struct msgb *trau_decode_efr(uint32_t callref, + const struct decoded_trau_frame *tf) +{ + struct msgb *msg; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, rc; + ubit_t check_bits[26]; + + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 31, + "GSM-DATA"); + if (!msg) + return NULL; + + frame = (struct gsm_data_frame *)msg->data; + memset(frame, 0, sizeof(struct gsm_data_frame)); + frame->msg_type = GSM_TCHF_FRAME_EFR; + frame->callref = callref; + msgb_put(msg, sizeof(struct gsm_data_frame) + 31); + + if (tf->c_bits[11]) /* BFI */ + goto bad_frame; + + data = frame->data; + data[0] = 0xc << 4; + /* reassemble d-bits */ + for (i = 1, j = 4; i < 39; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_1(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 26, + tf->d_bits + 39); + if (rc) + goto bad_frame; + for (i = 42, j = 42; i < 95; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_2(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 95); + if (rc) + goto bad_frame; + for (i = 98, j = 95; i < 148; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_3(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 148); + if (rc) + goto bad_frame; + for (i = 151, j = 145; i < 204; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_4(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 204); + if (rc) + goto bad_frame; + for (i = 207, j = 198; i < 257; i++, j++) + data[j/8] |= (tf->d_bits[i] << (7-(j%8))); + efr_parity_bits_5(check_bits, tf->d_bits); + rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 257); + if (rc) + goto bad_frame; + + return msg; + +bad_frame: + frame->msg_type = GSM_TCHF_BAD_FRAME; + + return msg; +} /* we get called by subchan_demux */ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, @@ -177,45 +335,25 @@ 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; + struct msgb *msg = NULL; /* frame shall be sent to upqueue */ if (!(ue = lookup_trau_upqueue(src_e1_ss))) return -EINVAL; if (!ue->callref) return -EINVAL; - if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check))) + if (!memcmp(tf.c_bits, c_bits_check_fr, 5)) + msg = trau_decode_fr(ue->callref, &tf); + else if (!memcmp(tf.c_bits, c_bits_check_efr, 5)) + msg = trau_decode_efr(ue->callref, &tf); + else { DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n", - osmo_hexdump(tf.c_bits, sizeof(c_bits_check))); - msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, - "GSM-DATA"); + osmo_hexdump(tf.c_bits, 5)); + DEBUGPC(DLMUX, "test trau (C1-C5) %s\n", + osmo_hexdump(c_bits_check_efr, 5)); + return -EINVAL; + } if (!msg) return -ENOMEM; - - 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; - msgb_put(msg, sizeof(struct gsm_data_frame) + 33); trau_tx_to_mncc(ue->net, msg); return 0; @@ -275,13 +413,86 @@ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref) return 0; } +void trau_encode_fr(struct decoded_trau_frame *tf, + const unsigned char *data) +{ + int i, j, k, l, o; + + /* 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++; + } +} + +void trau_encode_efr(struct decoded_trau_frame *tf, + const unsigned char *data) +{ + int i, j; + ubit_t check_bits[26]; + + /* set c-bits and t-bits */ + tf->c_bits[0] = 1; + tf->c_bits[1] = 1; + tf->c_bits[2] = 0; + tf->c_bits[3] = 1; + 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 */ + tf->d_bits[0] = 1; + for (i = 1, j = 4; i < 39; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_1(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 26, + tf->d_bits + 39); + for (i = 42, j = 42; i < 95; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_2(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 95); + for (i = 98, j = 95; i < 148; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_3(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 148); + for (i = 151, j = 145; i < 204; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_4(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, + tf->d_bits + 204); + for (i = 207, j = 198; i < 257; i++, j++) + tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; + efr_parity_bits_5(check_bits, tf->d_bits); + osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, + tf->d_bits + 257); +} + int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) { uint8_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); @@ -290,30 +501,10 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) 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++; - } + trau_encode_fr(&tf, frame->data); + break; + case GSM_TCHF_FRAME_EFR: + trau_encode_efr(&tf, frame->data); break; default: DEBUGPC(DLMUX, "unsupported message type %d\n", diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index 7f51bfa..d4bb954 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = gsm0408 db channel mgcp gprs si abis gbproxy +SUBDIRS = gsm0408 db channel mgcp gprs si abis gbproxy trau if BUILD_NAT SUBDIRS += bsc-nat bsc-nat-trie diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 76110f5..652cfe9 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -81,3 +81,9 @@ AT_KEYWORDS([gbproxy]) cat $abs_srcdir/gbproxy/gbproxy_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gbproxy/gbproxy_test], [], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([trau]) +AT_KEYWORDS([trau]) +cat $abs_srcdir/trau/trau_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/trau/trau_test], [], [expout], [ignore]) +AT_CLEANUP diff --git a/openbsc/tests/trau/Makefile.am b/openbsc/tests/trau/Makefile.am new file mode 100644 index 0000000..d4aa1c3 --- /dev/null +++ b/openbsc/tests/trau/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = trau_test.ok + +noinst_PROGRAMS = trau_test + +trau_test_SOURCES = trau_test.c +trau_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOGSM_LIBS) $(LIBSMPP34_LIBS) $(LIBOSMOVTY_LIBS) -ldl -ldbi + diff --git a/openbsc/tests/trau/trau_test.c b/openbsc/tests/trau/trau_test.c new file mode 100644 index 0000000..f8a48db --- /dev/null +++ b/openbsc/tests/trau/trau_test.c @@ -0,0 +1,82 @@ +/* (C) 2013 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include + +void test_trau_fr_efr(unsigned char *data) +{ + struct decoded_trau_frame tf; + struct msgb *msg; + struct gsm_data_frame *frame; + + printf("Testing TRAU FR transcoding.\n"); + data[0] = 0xd0; + trau_encode_fr(&tf, data); + tf.c_bits[11] = 0; /* clear BFI */ + msg = trau_decode_fr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_FRAME); + OSMO_ASSERT(!memcmp(frame->data, data, 33)); + msgb_free(msg); + + printf("Testing TRAU EFR transcoding.\n"); + data[0] = 0xc0; + trau_encode_efr(&tf, data); + OSMO_ASSERT(tf.d_bits[0] == 1); /* spare bit must be 1 */ + tf.c_bits[11] = 0; /* clear BFI */ + msg = trau_decode_efr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_FRAME_EFR); + OSMO_ASSERT(!memcmp(frame->data, data, 31)); + + printf("Testing TRAU EFR decoding with CRC error.\n"); + tf.d_bits[0] = 0; /* spare bit must be included */ + msg = trau_decode_efr(1, &tf); + OSMO_ASSERT(msg != NULL); + frame = (struct gsm_data_frame *)msg->data; + OSMO_ASSERT(frame->msg_type == GSM_TCHF_BAD_FRAME); + msgb_free(msg); +} + +int main() +{ + unsigned char data[33]; + int i; + + memset(data, 0x00, sizeof(data)); + test_trau_fr_efr(data); + memset(data, 0xff, sizeof(data)); + test_trau_fr_efr(data); + srandom(42); + for (i = 0; i < sizeof(data); i++) + data[i] = random(); + test_trau_fr_efr(data); + printf("Done\n"); + return 0; +} + +/* stubs */ +void vty_out() {} -- 1.7.3.4 --------------070405070402030304000504-- From holger at freyther.de Thu Dec 19 09:09:51 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 19 Dec 2013 10:09:51 +0100 Subject: [PATCH] Add EFR support to TRAU muxer + test case In-Reply-To: <52B2AC41.70005@eversberg.eu> References: <1386575124-28659-1-git-send-email-jolly@eversberg.eu> <20131218152303.GC13933@xiaoyu.lan> <52B2AC41.70005@eversberg.eu> Message-ID: <20131219090951.GA30292@xiaoyu.lan> On Thu, Dec 19, 2013 at 09:20:17AM +0100, Andreas Eversberg wrote: > +const struct osmo_crc8gen_code gsm0860_efr_crc3 = { I made that static. > + .bits = 3, Fixed the indent. > +EXTRA_DIST = trau_test.ok this file was missing from the patch. "make distcheck" would have found this. .gitignore needed an entry for tests/trau/trau_test From koue at chaosophia.net Thu Dec 12 14:36:09 2013 From: koue at chaosophia.net (koue at chaosophia.net) Date: 12 Dec 2013 16:36:09 +0200 Subject: OpenBSC FreeBSD compilation Message-ID: Hello list, I'm Nikola and I'm quite new to OpenBSC. Last two days I played little bit with OpenBSC compilation on FreeBSD and with few changes the work is done. I am sending the diff file. Not sure about __BYTE_ORDER declaration but hope someone can help (I am not developer). Regards, Nikola -------------- next part -------------- A non-text attachment was scrubbed... Name: openbsc-freebsd.diff Type: application/octet-stream Size: 4005 bytes Desc: openbsc-freebsd.diff URL: From holger at freyther.de Thu Dec 12 16:47:03 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 12 Dec 2013 17:47:03 +0100 Subject: OpenBSC FreeBSD compilation In-Reply-To: References: Message-ID: <20131212164703.GB21967@xiaoyu.lan> On Thu, Dec 12, 2013 at 04:36:09PM +0200, koue at chaosophia.net wrote: > Hello list, Hi! > I'm Nikola and I'm quite new to OpenBSC. Last two days I played > little bit with OpenBSC compilation on FreeBSD and with few changes > the work is done. I am sending the diff file. Not sure about > __BYTE_ORDER declaration but hope someone can help (I am not > developer). Oh? Does libosmo-abis and libosmo-sccp build on FreeBSD out of the box? The LIBRARY_DL change is correct, we need to think about the __BYTE_ORDER change. holger From koue at chaosophia.net Fri Dec 13 07:46:13 2013 From: koue at chaosophia.net (koue at chaosophia.net) Date: 13 Dec 2013 09:46:13 +0200 Subject: OpenBSC FreeBSD compilation In-Reply-To: <20131212164703.GB21967@xiaoyu.lan> References: <20131212164703.GB21967@xiaoyu.lan> Message-ID: On Dec 12 2013, Holger Hans Peter Freyther wrote: >On Thu, Dec 12, 2013 at 04:36:09PM +0200, koue at chaosophia.net wrote: >> Hello list, > >Hi! > >> I'm Nikola and I'm quite new to OpenBSC. Last two days I played >> little bit with OpenBSC compilation on FreeBSD and with few changes >> the work is done. I am sending the diff file. Not sure about >> __BYTE_ORDER declaration but hope someone can help (I am not >> developer). > >Oh? Does libosmo-abis and libosmo-sccp build on FreeBSD out of the >box? The LIBRARY_DL change is correct, we need to think about the >__BYTE_ORDER change. > Yes, libosmo-abis has been built successfully with ortp library from https://github.com/dyfet/ortp/ with no additional changes in the code of the abis lib. With ortp (0.13.0_1) from the ports tree there are errors during compilation. libosmo-sccp has NOT been built because OpenBSC does not complain about missing dependencies during compilation. At first look there are errors during compilation of libosmo-sccp. I will try to fix them. > >holger > > Nikola From koue at chaosophia.net Fri Dec 13 08:03:41 2013 From: koue at chaosophia.net (koue at chaosophia.net) Date: 13 Dec 2013 10:03:41 +0200 Subject: OpenBSC FreeBSD compilation In-Reply-To: References: <20131212164703.GB21967@xiaoyu.lan> Message-ID: On Dec 13 2013, koue at chaosophia.net wrote: >On Dec 12 2013, Holger Hans Peter Freyther wrote: > >>On Thu, Dec 12, 2013 at 04:36:09PM +0200, koue at chaosophia.net wrote: >>> Hello list, >> >>Hi! >> >>> I'm Nikola and I'm quite new to OpenBSC. Last two days I played >>> little bit with OpenBSC compilation on FreeBSD and with few changes >>> the work is done. I am sending the diff file. Not sure about >>> __BYTE_ORDER declaration but hope someone can help (I am not >>> developer). >> >>Oh? Does libosmo-abis and libosmo-sccp build on FreeBSD out of the >>box? The LIBRARY_DL change is correct, we need to think about the >>__BYTE_ORDER change. >> > >Yes, libosmo-abis has been built successfully with ortp library from >https://github.com/dyfet/ortp/ with no additional changes in the code of >the abis lib. With ortp (0.13.0_1) from the ports tree there are errors >during compilation. > >libosmo-sccp has NOT been built because OpenBSC does not complain about >missing dependencies during compilation. At first look there are errors >during compilation of libosmo-sccp. I will try to fix them. > libosmo-sccp has been built also with following patch. Forgot to mention: $ uname -a FreeBSD fbsd-osmocom 9.0-RELEASE FreeBSD 9.0-RELEASE #0: Tue Jan 3 07:15:25 UTC 2012 root at obrian.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC i386 >> >>holger >> >> > >Nikola > > -------------- next part -------------- A non-text attachment was scrubbed... Name: libosmo-sccp-freebsd.diff Type: application/octet-stream Size: 728 bytes Desc: libosmo-sccp-freebsd.diff URL: From holger at freyther.de Thu Dec 12 16:45:51 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 12 Dec 2013 17:45:51 +0100 Subject: abis_nm_pchan4chcomb API and return value Message-ID: <20131212164551.GA21967@xiaoyu.lan> Dear Harald, there is an open coverity issue in osmo-bts (CID 1040768) and it is about mixing enum types. enum abis_nm_chan_comb abis_nm_pchan4chcomb(uint8_t chcomb) From jerlbeck at sysmocom.de Fri Dec 13 12:18:19 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Fri, 13 Dec 2013 13:18:19 +0100 Subject: [PATCH 1/2] contrib/rtp: Enhance RTP replay tool Message-ID: <1386937100-23353-1-git-send-email-jerlbeck@sysmocom.de> This patch adds optional parameters to pass the state file, the destination address (default 127.0.0.1), the destination port (default 4000), the source port (default 0). So it is called as follows: gst rtp_replay.st -a [FILE [HOST [SOURCEPORT [DESTPORT]]]] In addition, nonexistant FILEs are no longer created but opened read-only instead. Sponsored-by: On-Waves ehf --- openbsc/contrib/rtp/rtp_replay.st | 10 +++++++--- openbsc/contrib/rtp/rtp_replay_shared.st | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/openbsc/contrib/rtp/rtp_replay.st b/openbsc/contrib/rtp/rtp_replay.st index 7fa9a4c..e26d073 100644 --- a/openbsc/contrib/rtp/rtp_replay.st +++ b/openbsc/contrib/rtp/rtp_replay.st @@ -7,11 +7,15 @@ FileStream fileIn: 'rtp_replay_shared.st'. Eval [ - | replay | + | replay file host dport | + file := Smalltalk arguments at: 1 ifAbsent: [ 'rtpstream.state' ]. + host := Smalltalk arguments at: 2 ifAbsent: [ '127.0.0.1' ]. + dport := (Smalltalk arguments at: 3 ifAbsent: [ '4000' ]) asInteger. + sport := (Smalltalk arguments at: 4 ifAbsent: [ '0' ]) asInteger. - replay := RTPReplay on: Smalltalk arguments first. + replay := RTPReplay on: file fromPort: sport. Transcript nextPutAll: 'Going to stream now'; nl. - replay streamAudio: '127.0.0.1' port: 4000. + replay streamAudio: host port: dport. ] diff --git a/openbsc/contrib/rtp/rtp_replay_shared.st b/openbsc/contrib/rtp/rtp_replay_shared.st index dd32aed..7b68c0f 100644 --- a/openbsc/contrib/rtp/rtp_replay_shared.st +++ b/openbsc/contrib/rtp/rtp_replay_shared.st @@ -42,8 +42,18 @@ Object subclass: RTPReplay [ file: aFile; yourself ] + RTPReplay class >> on: aFile fromPort: aPort [ + ^ self new + initialize: aPort; + file: aFile; yourself + ] + initialize [ - socket := Sockets.DatagramSocket new. + self initialize: 0. + ] + + initialize: aPort [ + socket := Sockets.DatagramSocket local: '0.0.0.0' port: aPort. ] file: aFile [ @@ -59,7 +69,7 @@ Object subclass: RTPReplay [ last_time := nil. last_image := nil. - file := FileStream open: filename. + file := FileStream open: filename mode: #read. "Send the payload" dest := Sockets.SocketAddress byName: aHost. -- 1.7.9.5 From jerlbeck at sysmocom.de Fri Dec 13 12:18:20 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Fri, 13 Dec 2013 13:18:20 +0100 Subject: [PATCH 2/2] contrib/rtp: Add tool to create RTP state files In-Reply-To: <1386937100-23353-1-git-send-email-jerlbeck@sysmocom.de> References: <1386937100-23353-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1386937100-23353-2-git-send-email-jerlbeck@sysmocom.de> This tool provides the following features: - Output formats: state, C arrays - Optionally take RTP payload from existing state files - Generate streams with RTP timestamp jumps and/or delays - Set/change SSRC or payload type Requires erlang to be installed. Example: Generate 300 packets, set playout time offset to 1s, set RTP timestamp offset to 8000 (1s), generate another 100 packets, the RTP payload is copied from rtp.state: ./gen_rtp_header.erl --type=98 --file=rtp.state -- 0 300 0 --delay=1.0 100 8000 Sponsored-by: On-Waves ehf --- openbsc/contrib/rtp/gen_rtp_header.erl | 392 ++++++++++++++++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100755 openbsc/contrib/rtp/gen_rtp_header.erl diff --git a/openbsc/contrib/rtp/gen_rtp_header.erl b/openbsc/contrib/rtp/gen_rtp_header.erl new file mode 100755 index 0000000..9f980b0 --- /dev/null +++ b/openbsc/contrib/rtp/gen_rtp_header.erl @@ -0,0 +1,392 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%%! -smp disable +-module(gen_rtp_header). + +% -mode(compile). + +-define(VERSION, "0.1"). + +-export([main/1]). + +-record(rtp_packet, + { + version = 2, + padding = 0, + marker = 0, + payload_type = 0, + seqno = 0, + timestamp = 0, + ssrc = 0, + csrcs = [], + extension = <<>>, + payload = <<>>, + realtime + }). + + +main(Args) -> + DefaultOpts = [{format, state}, + {ssrc, 16#11223344}, + {pt, 98}], + {PosArgs, Opts} = getopts_checked(Args, DefaultOpts), + log(debug, fun (Dev) -> + io:format(Dev, "Initial options:~n", []), + dump_opts(Dev, Opts), + io:format(Dev, "~s: ~p~n", ["Args", PosArgs]) + end, [], Opts), + main(PosArgs, Opts). + +main([First | RemArgs], Opts) -> + try + F = list_to_integer(First), + Format = proplists:get_value(format, Opts, state), + PayloadData = proplists:get_value(payload, Opts, undef), + InFile = proplists:get_value(file, Opts, undef), + + Payload = case {PayloadData, InFile} of + {undef, undef} -> #rtp_packet.payload; + {P, undef} -> P; + {_, File} -> + log(info, "Loading file '~s'~n", [File], Opts), + {ok, InDev} = file:open(File, [read]), + DS = [ Pl#rtp_packet.payload || {_T, Pl} <- read_packets(InDev, Opts)], + file:close(InDev), + log(debug, "File '~s' closed, ~w packets read.~n", [File, length(DS)], Opts), + DS + end, + Dev = standard_io, + write_packet_pre(Dev, Format), + do_groups(Dev, Payload, F, RemArgs, Opts), + write_packet_post(Dev, Format), + 0 + catch + _:_ -> + log(debug, "~p~n", [hd(erlang:get_stacktrace())], Opts), + usage(), + halt(1) + end + ; + +main(_, _Opts) -> + usage(), + halt(1). + +%%% group (count + offset) handling %%% + +do_groups(_Dev, _Pl, _F, [], _Opts) -> + ok; + +do_groups(Dev, Pl, F, [L], Opts) -> + do_groups(Dev, Pl, F, [L, 0], Opts); + +do_groups(Dev, Pl, First, [L, O | Args], Opts) -> + Ssrc = proplists:get_value(ssrc, Opts, #rtp_packet.ssrc), + PT = proplists:get_value(pt, Opts, #rtp_packet.payload_type), + Len = list_to_num(L), + Offs = list_to_num(O), + log(info, "Starting group: Ssrc=~.16B, PT=~B, First=~B, Len=~B, Offs=~B~n", + [Ssrc, PT, First, Len, Offs], Opts), + Pkg = #rtp_packet{ssrc = Ssrc, payload_type = PT}, + Pl2 = write_packets(Dev, Pl, Pkg, First, Len, Offs, Opts), + {Args2, Opts2} = getopts_checked(Args, Opts), + log(debug, fun (Io) -> + io:format(Io, "Changed options:~n", []), + dump_opts(Io, Opts2 -- Opts) + end, [], Opts), + do_groups(Dev, Pl2, First+Len, Args2, Opts2). + +%%% error handling helpers %%% + +getopts_checked(Args, Opts) -> + try + getopts(Args, Opts) + catch + C:R -> + log(error, "~s~n", + [explain_error(C, R, erlang:get_stacktrace(), Opts)], Opts), + usage(), + halt(1) + end. + +explain_error(error, badarg, [{erlang,list_to_integer,[S,B]} | _ ], _Opts) -> + io_lib:format("Invalid number '~s' (base ~B)", [S, B]); +explain_error(error, badarg, [{erlang,list_to_integer,[S]} | _ ], _Opts) -> + io_lib:format("Invalid decimal number '~s'", [S]); +explain_error(C, R, [Hd | _ ], _Opts) -> + io_lib:format("~p, ~p:~p", [Hd, C, R]); +explain_error(_, _, [], _Opts) -> + "". + +%%% usage and options %%% + +myname() -> + filename:basename(escript:script_name()). + +usage(Text) -> + io:format(standard_error, "~s: ~s~n", [myname(), Text]), + usage(). + +usage() -> + io:format(standard_error, + "Usage: ~s [Options] Start Count1 Offs1 [[Options] Count2 Offs2 ...]~n", + [myname()]). + +show_version() -> + io:format(standard_io, + "~s ~s~n", [myname(), ?VERSION]). + +show_help() -> + io:format(standard_io, + "Usage: ~s [Options] Start Count1 Offs1 [[Options] Count2 Offs2 ...]~n~n" ++ + "Options:~n" ++ + " -h, --help this text~n" ++ + " --version show version info~n" ++ + " -i, --file=FILE reads payload from state file~n" ++ + " -p, --payload=HEX set constant payload~n" ++ + " --verbose=N set verbosity~n" ++ + " -v increase verbosity~n" ++ + " --format=state use state format for output (default)~n" ++ + " -C, --format=c use simple C lines for output~n" ++ + " --format=carray use a C array for output~n" ++ + " -s, --ssrc=SSRC set the SSRC~n" ++ + " -t, --type=N set the payload type~n" ++ + " -d, --delay=FLOAT add offset to playout timestamp~n" ++ + "~n" ++ + "Arguments:~n" ++ + " Start initial packet (sequence) number~n" ++ + " Count number of packets~n" ++ + " Offs timestamp offset (in RTP units)~n" ++ + "", [myname()]). + +getopts([ "--file=" ++ File | R], Opts) -> + getopts(R, [{file, File} | Opts]); +getopts([ "-i" ++ T | R], Opts) -> + getopts_alias_arg("--file", T, R, Opts); +getopts([ "--version" | _], _Opts) -> + show_version(), + halt(0); +getopts([ "--help" | _], _Opts) -> + show_help(), + halt(0); +getopts([ "-h" ++ T | R], Opts) -> + getopts_alias_no_arg("--help", T, R, Opts); +getopts([ "--verbose=" ++ V | R], Opts) -> + Verbose = list_to_integer(V), + getopts(R, [{verbose, Verbose} | Opts]); +getopts([ "-v" ++ T | R], Opts) -> + Verbose = proplists:get_value(verbose, Opts, 0), + getopts_short_no_arg(T, R, [ {verbose, Verbose+1} | Opts]); +getopts([ "--format=state" | R], Opts) -> + getopts(R, [{format, state} | Opts]); +getopts([ "--format=c" | R], Opts) -> + getopts(R, [{format, c} | Opts]); +getopts([ "-C" ++ T | R], Opts) -> + getopts_alias_no_arg("--format=c", T, R, Opts); +getopts([ "--format=carray" | R], Opts) -> + getopts(R, [{format, carray} | Opts]); +getopts([ "--payload=" ++ Hex | R], Opts) -> + getopts(R, [{payload, hex_to_bin(Hex)} | Opts]); +getopts([ "--ssrc=" ++ Num | R], Opts) -> + getopts(R, [{ssrc, list_to_num(Num)} | Opts]); +getopts([ "-s" ++ T | R], Opts) -> + getopts_alias_arg("--ssrc", T, R, Opts); +getopts([ "--type=" ++ Num | R], Opts) -> + getopts(R, [{pt, list_to_num(Num)} | Opts]); +getopts([ "-t" ++ T | R], Opts) -> + getopts_alias_arg("--type", T, R, Opts); +getopts([ "--delay=" ++ Num | R], Opts) -> + getopts(R, [{delay, list_to_float(Num)} | Opts]); +getopts([ "-d" ++ T | R], Opts) -> + getopts_alias_arg("--delay", T, R, Opts); + +% parsing helpers +getopts([ "--" | R], Opts) -> + {R, normalize_opts(Opts)}; +getopts([ O = "--" ++ _ | _], _Opts) -> + usage("Invalid option: " ++ O), + halt(1); +getopts([ [ $-, C | _] | _], _Opts) when C < $0; C > $9 -> + usage("Invalid option: -" ++ [C]), + halt(1); + +getopts(R, Opts) -> + {R, normalize_opts(Opts)}. + +getopts_short_no_arg([], R, Opts) -> getopts(R, Opts); +getopts_short_no_arg(T, R, Opts) -> getopts([ "-" ++ T | R], Opts). + +getopts_alias_no_arg(A, [], R, Opts) -> getopts([A | R], Opts); +getopts_alias_no_arg(A, T, R, Opts) -> getopts([A, "-" ++ T | R], Opts). + +getopts_alias_arg(A, [], [T | R], Opts) -> getopts([A ++ "=" ++ T | R], Opts); +getopts_alias_arg(A, T, R, Opts) -> getopts([A ++ "=" ++ T | R], Opts). + +normalize_opts(Opts) -> + [ proplists:lookup(E, Opts) || E <- proplists:get_keys(Opts) ]. + +%%% conversions %%% + +bin_to_hex(Bin) -> [hd(integer_to_list(N,16)) || <> <= Bin]. +hex_to_bin(Hex) -> << <<(list_to_integer([Nib],16)):4>> || Nib <- Hex>>. + +list_to_num("-" ++ Str) -> -list_to_num(Str); +list_to_num("0x" ++ Str) -> list_to_integer(Str, 16); +list_to_num("0b" ++ Str) -> list_to_integer(Str, 2); +list_to_num(Str = [ $0 | _ ]) -> list_to_integer(Str, 8); +list_to_num(Str) -> list_to_integer(Str, 10). + +%%% dumping data %%% + +dump_opts(Dev, Opts) -> + dump_opts2(Dev, Opts, proplists:get_keys(Opts)). + +dump_opts2(Dev, Opts, [OptName | R]) -> + io:format(Dev, " ~-10s: ~p~n", + [OptName, proplists:get_value(OptName, Opts)]), + dump_opts2(Dev, Opts, R); +dump_opts2(_Dev, _Opts, []) -> ok. + +%%% logging %%% + +log(L, Fmt, Args, Opts) when is_list(Opts) -> + log(L, Fmt, Args, proplists:get_value(verbose, Opts, 0), Opts). + +log(debug, Fmt, Args, V, Opts) when V > 2 -> log2("DEBUG", Fmt, Args, Opts); +log(info, Fmt, Args, V, Opts) when V > 1 -> log2("INFO", Fmt, Args, Opts); +log(notice, Fmt, Args, V, Opts) when V > 0 -> log2("NOTICE", Fmt, Args, Opts); +log(warn, Fmt, Args, _V, Opts) -> log2("WARNING", Fmt, Args, Opts); +log(error, Fmt, Args, _V, Opts) -> log2("ERROR", Fmt, Args, Opts); + +log(Lvl, Fmt, Args, V, Opts) when V >= Lvl -> log2("", Fmt, Args, Opts); + +log(_, _, _, _i, _) -> ok. + +log2(Type, Fmt, Args, _Opts) when is_list(Fmt) -> + io:format(standard_error, "~s: " ++ Fmt, [Type | Args]); +log2("", Fmt, Args, _Opts) when is_list(Fmt) -> + io:format(standard_error, Fmt, Args); +log2(_Type, Fun, _Args, _Opts) when is_function(Fun, 1) -> + Fun(standard_error). + +%%% RTP packets %%% + +make_rtp_packet(P = #rtp_packet{version = 2}) -> + << (P#rtp_packet.version):2, + 0:1, % P + 0:1, % X + 0:4, % CC + (P#rtp_packet.marker):1, + (P#rtp_packet.payload_type):7, + (P#rtp_packet.seqno):16, + (P#rtp_packet.timestamp):32, + (P#rtp_packet.ssrc):32, + (P#rtp_packet.payload)/bytes + >>. + +parse_rtp_packet( + << 2:2, % Version 2 + 0:1, % P (not supported yet) + 0:1, % X (not supported yet) + 0:4, % CC (not supported yet) + M:1, + PT:7, + SeqNo: 16, + TS:32, + Ssrc:32, + Payload/bytes >>) -> + #rtp_packet{ + version = 0, + marker = M, + payload_type = PT, + seqno = SeqNo, + timestamp = TS, + ssrc = Ssrc, + payload = Payload}. + +%%% payload generation %%% + +next_payload(F) when is_function(F) -> + {F(), F}; +next_payload({F, D}) when is_function(F) -> + {P, D2} = F(D), + {P, {F, D2}}; +next_payload([P | R]) -> + {P, R}; +next_payload([]) -> + undef; +next_payload(Bin = <<_/bytes>>) -> + {Bin, Bin}. + +%%% real writing work %%% + +write_packets(_Dev, DS, _P, _F, 0, _O, _Opts) -> + DS; +write_packets(Dev, DataSource, P = #rtp_packet{}, F, L, O, Opts) -> + Format = proplists:get_value(format, Opts, state), + Ptime = proplists:get_value(duration, Opts, 160), + Delay = proplists:get_value(delay, Opts, 0), + case next_payload(DataSource) of + {Payload, DataSource2} -> + write_packet(Dev, 0.020 * F + Delay, + P#rtp_packet{seqno = F, timestamp = F*Ptime+O, + payload = Payload}, + Format), + write_packets(Dev, DataSource2, P, F+1, L-1, O, Opts); + Other -> Other + end. + +write_packet(Dev, Time, P = #rtp_packet{}, Format) -> + Bin = make_rtp_packet(P), + + write_packet_line(Dev, Time, P, Bin, Format). + +write_packet_pre(Dev, carray) -> + io:format(Dev, + "struct {float t; int len; char *data;} packets[] = {~n", []); + +write_packet_pre(_Dev, _) -> ok. + +write_packet_post(Dev, carray) -> + io:format(Dev, "};~n", []); + +write_packet_post(_Dev, _) -> ok. + +write_packet_line(Dev, Time, _P, Bin, state) -> + io:format(Dev, "~f ~s~n", [Time, bin_to_hex(Bin)]); + +write_packet_line(Dev, Time, #rtp_packet{seqno = N, timestamp = TS}, Bin, c) -> + ByteList = [ [ $0, $x | integer_to_list(Byte, 16) ] || <> <= Bin ], + ByteStr = string:join(ByteList, ", "), + io:format(Dev, "/* time=~f, SeqNo=~B, TS=~B */ {~s}~n", [Time, N, TS, ByteStr]); + +write_packet_line(Dev, Time, #rtp_packet{seqno = N, timestamp = TS}, Bin, carray) -> + io:format(Dev, " /* RTP: SeqNo=~B, TS=~B */~n", [N, TS]), + io:format(Dev, " {~f, ~B, \"", [Time, size(Bin)]), + [ io:format(Dev, "\\x~2.16.0B", [Byte]) || <> <= Bin ], + io:format(Dev, "\"},~n", []). + +%%% real reading work %%% + +read_packets(Dev, Opts) -> + Format = proplists:get_value(in_format, Opts, state), + + read_packets(Dev, Opts, Format). + +read_packets(Dev, Opts, Format) -> + case read_packet(Dev, Format) of + eof -> []; + Tuple -> [Tuple | read_packets(Dev, Opts, Format)] + end. + +read_packet(Dev, Format) -> + case read_packet_line(Dev, Format) of + {Time, Bin} -> {Time, parse_rtp_packet(Bin)}; + eof -> eof + end. + +read_packet_line(Dev, state) -> + case io:fread(Dev, "", "~f ~s") of + {ok, [Time, Hex]} -> {Time, hex_to_bin(Hex)}; + eof -> eof + end. -- 1.7.9.5 From mailman-bounces at lists.osmocom.org Tue Dec 17 11:52:31 2013 From: mailman-bounces at lists.osmocom.org (mailman-bounces at lists.osmocom.org) Date: Tue, 17 Dec 2013 12:52:31 +0100 Subject: Uncaught bounce notification Message-ID: The attached message was received as a bounce, but either the bounce format was not recognized, or no member addresses could be extracted from it. This mailing list has been configured to send all unrecognized bounce messages to the list administrator(s). For more information see: https://lists.osmocom.org/mailman/admin/openbsc/bounce -------------- next part -------------- An embedded message was scrubbed... From: "Pierre Kim" Subject: nanoBTS reset tutorial Date: Tue, 17 Dec 2013 20:52:20 +0900 Size: 4721 URL: From admin at manateeshome.com Tue Dec 17 18:48:12 2013 From: admin at manateeshome.com (Pierre Kim) Date: Wed, 18 Dec 2013 03:48:12 +0900 Subject: nanoBTS reset procedure Message-ID: <094f01cefb58$8b144ba0$a13ce2e0$@manateeshome.com> Hello, For those who use nanoBTS for OpenBSC, I made a short video about how to properly reset the nanoBTS when something goes wrong. In the video demonstrates how to manually shirt the TIB pins For 110 series (Oval Shaped) : http://www.youtube.com/watch?v=VjW2EAP7wss For 165 series (Rectangular) : http://www.youtube.com/watch?v=9H-H5J8XxOE And happy holidays to all Cheers, Pierre -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew at carrierdetect.com Tue Dec 17 18:52:04 2013 From: andrew at carrierdetect.com (Andrew Back) Date: Tue, 17 Dec 2013 18:52:04 +0000 Subject: Failing SuperSIM (fakemagicsim) cards? Message-ID: Has anyone experienced these cards (as can be found on eBay) failing after a matter of months? I have some I programmed ~1 year ago and which worked fine in a bunch of Calypso handsets with NITB, but now I get "SIM card absent" whenever I try to make a call (but LUR is accepted OK and can see programmed IMSI in NITB debug). An operator SIM and an R&S test SIM is working fine with the setup. Also tried reprogramming the SuperSIM card (command used below). Regards, Andrew // ./pySim-prog.py -d /dev/ttyUSB0 -n OpenBSC -c 44 -x 001 -y 01 -i 001010000000053 -s 1234567890000000003 -k DEADBEEF0C0FFEE0F00D013370D00F03 Insert card now (or CTRL-C to cancel) Autodetected card type fakemagicsim Generated card parameters : > Name : OpenBSC > SMSP : e1ffffffffffffffffffffffff058100445555ffffffffffff000000 > ICCID : 1234567890000000003 > MCC/MNC : 1/1 > IMSI : 001010000000053 > Ki : DEADBEEF0C0FFEE0F00D013370D00F03 > OPC : 56078bafca0ce95e81089288cbfb296e Programming ... Done ! -- Andrew Back http://carrierdetect.com From jerlbeck at sysmocom.de Wed Dec 18 11:43:18 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Wed, 18 Dec 2013 12:43:18 +0100 Subject: [PATCH] contrib/rtp: Fix default payload case Message-ID: <1387366999-5087-1-git-send-email-jerlbeck@sysmocom.de> There is the wrong record field selection being used to extract the default value. It returns the tuple offset instead of the value. This patch fixes this. Sponsored-by: On-Waves ehf --- openbsc/contrib/rtp/gen_rtp_header.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openbsc/contrib/rtp/gen_rtp_header.erl b/openbsc/contrib/rtp/gen_rtp_header.erl index 9f980b0..cc4c61c 100755 --- a/openbsc/contrib/rtp/gen_rtp_header.erl +++ b/openbsc/contrib/rtp/gen_rtp_header.erl @@ -45,7 +45,9 @@ main([First | RemArgs], Opts) -> InFile = proplists:get_value(file, Opts, undef), Payload = case {PayloadData, InFile} of - {undef, undef} -> #rtp_packet.payload; + {undef, undef} -> + % use default value + #rtp_packet{}#rtp_packet.payload; {P, undef} -> P; {_, File} -> log(info, "Loading file '~s'~n", [File], Opts), -- 1.7.9.5 From jerlbeck at sysmocom.de Wed Dec 18 11:54:49 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Wed, 18 Dec 2013 12:54:49 +0100 Subject: [PATCH 1/3] mgcp/test: Add test cases for constant RTP sequence number Message-ID: <1387367691-17503-1-git-send-email-jerlbeck@sysmocom.de> This adds two test cases: 1. Packet repetition (dSeq=0, dTS=0) 2. Broken seqNo (dSeq=0, dTS=160) The second had been already present in the test cases, but it was a mere copy&paste mistake which turned out to be rather helpful. This patch therefore turns it into a documented test case. Sponsored-by: On-Waves ehf --- openbsc/tests/mgcp/mgcp_test.c | 22 +++++++++++++++++++++- openbsc/tests/mgcp/mgcp_test.ok | 32 ++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 9777a4d..b93ce7e 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -605,7 +605,27 @@ struct rtp_packet_info test_rtp_packets1[] = { {0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\x58\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=21, TS=36088 */ - {0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\xF8\x10\x20\x30\x40" + {0.380000, 20, "\x80\x62\x00\x15\x00\x00\x8C\xF8\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* Repeat last packet */ + /* RTP: SeqNo=21, TS=36088 */ + {0.400000, 20, "\x80\x62\x00\x15\x00\x00\x8C\xF8\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* RTP: SeqNo=22, TS=36248 */ + {0.420000, 20, "\x80\x62\x00\x16\x00\x00\x8D\x98\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* RTP: SeqNo=23, TS=36408 */ + {0.440000, 20, "\x80\x62\x00\x17\x00\x00\x8E\x38\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* Don't increment SeqNo but increment timestamp by 160 */ + /* RTP: SeqNo=23, TS=36568 */ + {0.460000, 20, "\x80\x62\x00\x17\x00\x00\x8E\xD8\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* RTP: SeqNo=24, TS=36728 */ + {0.480000, 20, "\x80\x62\x00\x18\x00\x00\x8F\x78\x10\x20\x30\x40" + "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, + /* RTP: SeqNo=25, TS=36888 */ + {0.500000, 20, "\x80\x62\x00\x19\x00\x00\x90\x18\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, }; diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index f9dd7cb..783f3a5 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -85,7 +85,13 @@ TS: 2480, dTS: 160, TS Errs: in 7, out 7 TS: 2640, dTS: 160, TS Errs: in 7, out 7 TS: 2960, dTS: 160, TS Errs: in 7, out 7 TS: 3120, dTS: 160, TS Errs: in 7, out 7 -TS: 3280, dTS: 160, TS Errs: in 8, out 8 +TS: 3280, dTS: 160, TS Errs: in 7, out 7 +TS: 3280, dTS: 160, TS Errs: in 7, out 7 +TS: 3440, dTS: 160, TS Errs: in 7, out 7 +TS: 3600, dTS: 160, TS Errs: in 7, out 7 +TS: 3760, dTS: 160, TS Errs: in 8, out 8 +TS: 3920, dTS: 160, TS Errs: in 8, out 8 +TS: 4080, dTS: 160, TS Errs: in 8, out 8 Testing packet error detection. Output SSRC changed to 11223344 TS: 0, dTS: 0, TS Errs: in 0, out 0 @@ -109,7 +115,13 @@ TS: 35288, dTS: 160, TS Errs: in 7, out 7 TS: 35448, dTS: 160, TS Errs: in 7, out 7 TS: 35768, dTS: 160, TS Errs: in 7, out 7 TS: 35928, dTS: 160, TS Errs: in 7, out 7 -TS: 36088, dTS: 160, TS Errs: in 8, out 8 +TS: 36088, dTS: 160, TS Errs: in 7, out 7 +TS: 36088, dTS: 160, TS Errs: in 7, out 7 +TS: 36248, dTS: 160, TS Errs: in 7, out 7 +TS: 36408, dTS: 160, TS Errs: in 7, out 7 +TS: 36568, dTS: 160, TS Errs: in 8, out 8 +TS: 36728, dTS: 160, TS Errs: in 8, out 8 +TS: 36888, dTS: 160, TS Errs: in 8, out 8 Testing packet error detection, patch timestamps. Output SSRC changed to 11223344 TS: 0, dTS: 0, TS Errs: in 0, out 0 @@ -133,7 +145,13 @@ TS: 35368, dTS: 160, TS Errs: in 7, out 0 TS: 35528, dTS: 160, TS Errs: in 7, out 0 TS: 35848, dTS: 160, TS Errs: in 7, out 0 TS: 36008, dTS: 160, TS Errs: in 7, out 0 -TS: 36008, dTS: 160, TS Errs: in 8, out 0 +TS: 36168, dTS: 160, TS Errs: in 7, out 0 +TS: 36168, dTS: 160, TS Errs: in 7, out 0 +TS: 36328, dTS: 160, TS Errs: in 7, out 0 +TS: 36488, dTS: 160, TS Errs: in 7, out 0 +TS: 36488, dTS: 160, TS Errs: in 8, out 0 +TS: 36648, dTS: 160, TS Errs: in 8, out 0 +TS: 36808, dTS: 160, TS Errs: in 8, out 0 Testing packet error detection, patch SSRC, patch timestamps. Output SSRC changed to 11223344 TS: 0, dTS: 0, TS Errs: in 0, out 0 @@ -156,5 +174,11 @@ TS: 2560, dTS: 160, TS Errs: in 7, out 0 TS: 2720, dTS: 160, TS Errs: in 7, out 0 TS: 3040, dTS: 160, TS Errs: in 7, out 0 TS: 3200, dTS: 160, TS Errs: in 7, out 0 -TS: 3200, dTS: 160, TS Errs: in 8, out 0 +TS: 3360, dTS: 160, TS Errs: in 7, out 0 +TS: 3360, dTS: 160, TS Errs: in 7, out 0 +TS: 3520, dTS: 160, TS Errs: in 7, out 0 +TS: 3680, dTS: 160, TS Errs: in 7, out 0 +TS: 3680, dTS: 160, TS Errs: in 8, out 0 +TS: 3840, dTS: 160, TS Errs: in 8, out 0 +TS: 4000, dTS: 160, TS Errs: in 8, out 0 Done -- 1.7.9.5 From jerlbeck at sysmocom.de Wed Dec 18 11:54:50 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Wed, 18 Dec 2013 12:54:50 +0100 Subject: [PATCH 2/3] mgcp/test: Use differential output for counters and timestamp In-Reply-To: <1387367691-17503-1-git-send-email-jerlbeck@sysmocom.de> References: <1387367691-17503-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1387367691-17503-2-git-send-email-jerlbeck@sysmocom.de> Currently the counter and output timestamp values are written out for each packet. This makes it difficult to see in the diffs what has been changed significantly. This patch changes this by showing differences for those values. The absolute input values are also shown now. In addition, the sequence numbers (the difference for the output value) are written, too. Sponsored-by: On-Waves ehf --- openbsc/tests/mgcp/mgcp_test.c | 24 ++- openbsc/tests/mgcp/mgcp_test.ok | 324 ++++++++++++++++++++++++++------------- 2 files changed, 236 insertions(+), 112 deletions(-) diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index b93ce7e..8b1b924 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -644,6 +644,10 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) struct sockaddr_in addr = {0}; char buffer[4096]; uint32_t last_ssrc = 0; + uint32_t last_timestamp = 0; + uint32_t last_seqno = 0; + int last_in_ts_err_cnt = 0; + int last_out_ts_err_cnt = 0; printf("Testing packet error detection%s%s.\n", patch_ssrc ? ", patch SSRC" : "", @@ -687,11 +691,23 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) last_ssrc = state.out_stream.ssrc; } - printf("TS: %d, dTS: %d, TS Errs: in %d, out %d\n", - state.out_stream.last_timestamp, + printf("In TS: %d, dTS: %d, Seq: %d\n", + state.in_stream.last_timestamp, + state.in_stream.last_tsdelta, + state.in_stream.last_seq); + + printf("Out TS change: %d, dTS: %d, Seq change: %d, " + "TS Err change: in %+d, out %+d\n", + state.out_stream.last_timestamp - last_timestamp, state.out_stream.last_tsdelta, - state.in_stream.err_ts_counter, - state.out_stream.err_ts_counter); + state.out_stream.last_seq - last_seqno, + state.in_stream.err_ts_counter - last_in_ts_err_cnt, + state.out_stream.err_ts_counter - last_out_ts_err_cnt); + + last_in_ts_err_cnt = state.in_stream.err_ts_counter; + last_out_ts_err_cnt = state.out_stream.err_ts_counter; + last_timestamp = state.out_stream.last_timestamp; + last_seqno = state.out_stream.last_seq; } } diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 783f3a5..03c1cf0 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -65,120 +65,228 @@ Parsing result: 0 Parsing result: 0 Testing packet error detection, patch SSRC. Output SSRC changed to 11223344 -TS: 0, dTS: 0, TS Errs: in 0, out 0 -TS: 160, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 1, out 1 -TS: 480, dTS: 160, TS Errs: in 1, out 1 -TS: 640, dTS: 160, TS Errs: in 1, out 1 -TS: 960, dTS: 320, TS Errs: in 2, out 2 -TS: 1120, dTS: 160, TS Errs: in 3, out 3 -TS: 1280, dTS: 160, TS Errs: in 3, out 3 -TS: 1400, dTS: 120, TS Errs: in 4, out 4 -TS: 1560, dTS: 160, TS Errs: in 5, out 5 -TS: 1720, dTS: 160, TS Errs: in 5, out 5 -TS: 1880, dTS: 160, TS Errs: in 5, out 5 -TS: 2040, dTS: 160, TS Errs: in 5, out 5 -TS: 2200, dTS: 160, TS Errs: in 5, out 5 -TS: 2320, dTS: 120, TS Errs: in 6, out 6 -TS: 2480, dTS: 160, TS Errs: in 7, out 7 -TS: 2640, dTS: 160, TS Errs: in 7, out 7 -TS: 2960, dTS: 160, TS Errs: in 7, out 7 -TS: 3120, dTS: 160, TS Errs: in 7, out 7 -TS: 3280, dTS: 160, TS Errs: in 7, out 7 -TS: 3280, dTS: 160, TS Errs: in 7, out 7 -TS: 3440, dTS: 160, TS Errs: in 7, out 7 -TS: 3600, dTS: 160, TS Errs: in 7, out 7 -TS: 3760, dTS: 160, TS Errs: in 8, out 8 -TS: 3920, dTS: 160, TS Errs: in 8, out 8 -TS: 4080, dTS: 160, TS Errs: in 8, out 8 +In TS: 0, dTS: 0, Seq: 0 +Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 +In TS: 160, dTS: 160, Seq: 1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 320, dTS: 160, Seq: 2 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 320, dTS: 160, Seq: 3 +Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +In TS: 480, dTS: 160, Seq: 4 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 640, dTS: 160, Seq: 5 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 960, dTS: 320, Seq: 6 +Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +1, out +1 +In TS: 1120, dTS: 160, Seq: 7 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +In TS: 1280, dTS: 160, Seq: 8 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 1400, dTS: 120, Seq: 9 +Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 +In TS: 1560, dTS: 160, Seq: 10 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +In TS: 1720, dTS: 160, Seq: 11 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 34688, dTS: 0, Seq: 12 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 34848, dTS: 160, Seq: 13 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35008, dTS: 160, Seq: 14 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35128, dTS: 120, Seq: 15 +Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 +In TS: 35288, dTS: 160, Seq: 16 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +In TS: 35448, dTS: 160, Seq: 17 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35768, dTS: 160, Seq: 19 +Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 +In TS: 35928, dTS: 160, Seq: 20 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36088, dTS: 160, Seq: 21 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36088, dTS: 160, Seq: 21 +Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 +In TS: 36248, dTS: 160, Seq: 22 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36408, dTS: 160, Seq: 23 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36568, dTS: 160, Seq: 23 +Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 +In TS: 36728, dTS: 160, Seq: 24 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36888, dTS: 160, Seq: 25 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Testing packet error detection. Output SSRC changed to 11223344 -TS: 0, dTS: 0, TS Errs: in 0, out 0 -TS: 160, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 1, out 1 -TS: 480, dTS: 160, TS Errs: in 1, out 1 -TS: 640, dTS: 160, TS Errs: in 1, out 1 -TS: 960, dTS: 320, TS Errs: in 2, out 2 -TS: 1120, dTS: 160, TS Errs: in 3, out 3 -TS: 1280, dTS: 160, TS Errs: in 3, out 3 -TS: 1400, dTS: 120, TS Errs: in 4, out 4 -TS: 1560, dTS: 160, TS Errs: in 5, out 5 -TS: 1720, dTS: 160, TS Errs: in 5, out 5 +In TS: 0, dTS: 0, Seq: 0 +Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 +In TS: 160, dTS: 160, Seq: 1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 320, dTS: 160, Seq: 2 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 320, dTS: 160, Seq: 3 +Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +In TS: 480, dTS: 160, Seq: 4 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 640, dTS: 160, Seq: 5 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 960, dTS: 320, Seq: 6 +Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +1, out +1 +In TS: 1120, dTS: 160, Seq: 7 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +In TS: 1280, dTS: 160, Seq: 8 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 1400, dTS: 120, Seq: 9 +Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 +In TS: 1560, dTS: 160, Seq: 10 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +In TS: 1720, dTS: 160, Seq: 11 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Output SSRC changed to 10203040 -TS: 34688, dTS: 160, TS Errs: in 5, out 5 -TS: 34848, dTS: 160, TS Errs: in 5, out 5 -TS: 35008, dTS: 160, TS Errs: in 5, out 5 -TS: 35128, dTS: 120, TS Errs: in 6, out 6 -TS: 35288, dTS: 160, TS Errs: in 7, out 7 -TS: 35448, dTS: 160, TS Errs: in 7, out 7 -TS: 35768, dTS: 160, TS Errs: in 7, out 7 -TS: 35928, dTS: 160, TS Errs: in 7, out 7 -TS: 36088, dTS: 160, TS Errs: in 7, out 7 -TS: 36088, dTS: 160, TS Errs: in 7, out 7 -TS: 36248, dTS: 160, TS Errs: in 7, out 7 -TS: 36408, dTS: 160, TS Errs: in 7, out 7 -TS: 36568, dTS: 160, TS Errs: in 8, out 8 -TS: 36728, dTS: 160, TS Errs: in 8, out 8 -TS: 36888, dTS: 160, TS Errs: in 8, out 8 +In TS: 34688, dTS: 0, Seq: 12 +Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 34848, dTS: 160, Seq: 13 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35008, dTS: 160, Seq: 14 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35128, dTS: 120, Seq: 15 +Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 +In TS: 35288, dTS: 160, Seq: 16 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +In TS: 35448, dTS: 160, Seq: 17 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35768, dTS: 160, Seq: 19 +Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 +In TS: 35928, dTS: 160, Seq: 20 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36088, dTS: 160, Seq: 21 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36088, dTS: 160, Seq: 21 +Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 +In TS: 36248, dTS: 160, Seq: 22 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36408, dTS: 160, Seq: 23 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36568, dTS: 160, Seq: 23 +Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 +In TS: 36728, dTS: 160, Seq: 24 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36888, dTS: 160, Seq: 25 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Testing packet error detection, patch timestamps. Output SSRC changed to 11223344 -TS: 0, dTS: 0, TS Errs: in 0, out 0 -TS: 160, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 0, out 0 -TS: 480, dTS: 160, TS Errs: in 1, out 0 -TS: 640, dTS: 160, TS Errs: in 1, out 0 -TS: 800, dTS: 160, TS Errs: in 1, out 0 -TS: 960, dTS: 160, TS Errs: in 2, out 0 -TS: 1120, dTS: 160, TS Errs: in 3, out 0 -TS: 1280, dTS: 160, TS Errs: in 3, out 0 -TS: 1440, dTS: 160, TS Errs: in 4, out 0 -TS: 1600, dTS: 160, TS Errs: in 5, out 0 -TS: 1760, dTS: 160, TS Errs: in 5, out 0 +In TS: 0, dTS: 0, Seq: 0 +Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 +In TS: 160, dTS: 160, Seq: 1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 320, dTS: 160, Seq: 2 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 320, dTS: 160, Seq: 3 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 480, dTS: 160, Seq: 4 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 640, dTS: 160, Seq: 5 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 960, dTS: 320, Seq: 6 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 1120, dTS: 160, Seq: 7 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 1280, dTS: 160, Seq: 8 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 1400, dTS: 120, Seq: 9 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 1560, dTS: 160, Seq: 10 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 1720, dTS: 160, Seq: 11 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Output SSRC changed to 10203040 -TS: 34728, dTS: 160, TS Errs: in 5, out 0 -TS: 34888, dTS: 160, TS Errs: in 5, out 0 -TS: 35048, dTS: 160, TS Errs: in 5, out 0 -TS: 35208, dTS: 160, TS Errs: in 6, out 0 -TS: 35368, dTS: 160, TS Errs: in 7, out 0 -TS: 35528, dTS: 160, TS Errs: in 7, out 0 -TS: 35848, dTS: 160, TS Errs: in 7, out 0 -TS: 36008, dTS: 160, TS Errs: in 7, out 0 -TS: 36168, dTS: 160, TS Errs: in 7, out 0 -TS: 36168, dTS: 160, TS Errs: in 7, out 0 -TS: 36328, dTS: 160, TS Errs: in 7, out 0 -TS: 36488, dTS: 160, TS Errs: in 7, out 0 -TS: 36488, dTS: 160, TS Errs: in 8, out 0 -TS: 36648, dTS: 160, TS Errs: in 8, out 0 -TS: 36808, dTS: 160, TS Errs: in 8, out 0 +In TS: 34688, dTS: 0, Seq: 12 +Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 34848, dTS: 160, Seq: 13 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35008, dTS: 160, Seq: 14 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35128, dTS: 120, Seq: 15 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 35288, dTS: 160, Seq: 16 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 35448, dTS: 160, Seq: 17 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35768, dTS: 160, Seq: 19 +Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 +In TS: 35928, dTS: 160, Seq: 20 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36088, dTS: 160, Seq: 21 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36088, dTS: 160, Seq: 21 +Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 +In TS: 36248, dTS: 160, Seq: 22 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36408, dTS: 160, Seq: 23 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36568, dTS: 160, Seq: 23 +Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +1, out +0 +In TS: 36728, dTS: 160, Seq: 24 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36888, dTS: 160, Seq: 25 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Testing packet error detection, patch SSRC, patch timestamps. Output SSRC changed to 11223344 -TS: 0, dTS: 0, TS Errs: in 0, out 0 -TS: 160, dTS: 160, TS Errs: in 0, out 0 -TS: 320, dTS: 160, TS Errs: in 0, out 0 -TS: 480, dTS: 160, TS Errs: in 1, out 0 -TS: 640, dTS: 160, TS Errs: in 1, out 0 -TS: 800, dTS: 160, TS Errs: in 1, out 0 -TS: 960, dTS: 160, TS Errs: in 2, out 0 -TS: 1120, dTS: 160, TS Errs: in 3, out 0 -TS: 1280, dTS: 160, TS Errs: in 3, out 0 -TS: 1440, dTS: 160, TS Errs: in 4, out 0 -TS: 1600, dTS: 160, TS Errs: in 5, out 0 -TS: 1760, dTS: 160, TS Errs: in 5, out 0 -TS: 1920, dTS: 160, TS Errs: in 5, out 0 -TS: 2080, dTS: 160, TS Errs: in 5, out 0 -TS: 2240, dTS: 160, TS Errs: in 5, out 0 -TS: 2400, dTS: 160, TS Errs: in 6, out 0 -TS: 2560, dTS: 160, TS Errs: in 7, out 0 -TS: 2720, dTS: 160, TS Errs: in 7, out 0 -TS: 3040, dTS: 160, TS Errs: in 7, out 0 -TS: 3200, dTS: 160, TS Errs: in 7, out 0 -TS: 3360, dTS: 160, TS Errs: in 7, out 0 -TS: 3360, dTS: 160, TS Errs: in 7, out 0 -TS: 3520, dTS: 160, TS Errs: in 7, out 0 -TS: 3680, dTS: 160, TS Errs: in 7, out 0 -TS: 3680, dTS: 160, TS Errs: in 8, out 0 -TS: 3840, dTS: 160, TS Errs: in 8, out 0 -TS: 4000, dTS: 160, TS Errs: in 8, out 0 +In TS: 0, dTS: 0, Seq: 0 +Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 +In TS: 160, dTS: 160, Seq: 1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 320, dTS: 160, Seq: 2 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 320, dTS: 160, Seq: 3 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 480, dTS: 160, Seq: 4 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 640, dTS: 160, Seq: 5 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 960, dTS: 320, Seq: 6 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 1120, dTS: 160, Seq: 7 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 1280, dTS: 160, Seq: 8 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 1400, dTS: 120, Seq: 9 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 1560, dTS: 160, Seq: 10 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 1720, dTS: 160, Seq: 11 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 34688, dTS: 0, Seq: 12 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 34848, dTS: 160, Seq: 13 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35008, dTS: 160, Seq: 14 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35128, dTS: 120, Seq: 15 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 35288, dTS: 160, Seq: 16 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +In TS: 35448, dTS: 160, Seq: 17 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 35768, dTS: 160, Seq: 19 +Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 +In TS: 35928, dTS: 160, Seq: 20 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36088, dTS: 160, Seq: 21 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36088, dTS: 160, Seq: 21 +Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 +In TS: 36248, dTS: 160, Seq: 22 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36408, dTS: 160, Seq: 23 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36568, dTS: 160, Seq: 23 +Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +1, out +0 +In TS: 36728, dTS: 160, Seq: 24 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 +In TS: 36888, dTS: 160, Seq: 25 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Done -- 1.7.9.5 From jerlbeck at sysmocom.de Wed Dec 18 11:54:51 2013 From: jerlbeck at sysmocom.de (Jacob Erlbeck) Date: Wed, 18 Dec 2013 12:54:51 +0100 Subject: [PATCH 3/3] mgcp/rtp: Only patch timestamp alignment errors In-Reply-To: <1387367691-17503-1-git-send-email-jerlbeck@sysmocom.de> References: <1387367691-17503-1-git-send-email-jerlbeck@sysmocom.de> Message-ID: <1387367691-17503-3-git-send-email-jerlbeck@sysmocom.de> Currently, all timestamps are force to SeqNo*d + C which is more than required by the nanoBTS which seems to be sensitive to alignment errors only (dTS != k*d, d = ptime * rate = 160). This patch replaces the force_constant_timing feature by a force_aligned_timing feature. The timestamp offset will only be changed (and timestamp errors counted) when the alignment does not match to the raster based on ptime (default 20ms). The VTY interface does not change. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/mgcp.h | 2 +- openbsc/include/openbsc/mgcp_internal.h | 2 +- openbsc/src/libmgcp/mgcp_network.c | 145 ++++++++++++++++++++++--------- openbsc/src/libmgcp/mgcp_protocol.c | 4 +- openbsc/src/libmgcp/mgcp_vty.c | 20 ++--- openbsc/tests/mgcp/mgcp_test.c | 2 +- openbsc/tests/mgcp/mgcp_test.ok | 44 +++++----- 7 files changed, 143 insertions(+), 76 deletions(-) diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index 0d64590..335c83d 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -121,7 +121,7 @@ struct mgcp_trunk_config { /* RTP patching */ int force_constant_ssrc; /* 0: don't, 1: once */ - int force_constant_timing; + int force_aligned_timing; /* spec handling */ int force_realloc; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index 20c433a..a9ae33c 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -87,7 +87,7 @@ struct mgcp_rtp_end { /* RTP patching */ int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ - int force_constant_timing; + int force_aligned_timing; /* * Each end has a socket... diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 40227af..657019e 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -134,29 +134,47 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp) endp->net_end.rtp_port, buf, 1); } +static int32_t compute_timestamp_aligment_error(struct mgcp_rtp_stream_state *sstate, + int ptime, uint32_t timestamp) +{ + int32_t timestamp_delta; + + if (ptime == 0) + return 0; + + /* Align according to: T - Tlast = k * Tptime */ + timestamp_delta = timestamp - sstate->last_timestamp; + + return timestamp_delta % ptime; +} + static int check_rtp_timestamp(struct mgcp_endpoint *endp, - struct mgcp_rtp_stream_state *state, + struct mgcp_rtp_state *state, + struct mgcp_rtp_stream_state *sstate, struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, uint16_t seq, uint32_t timestamp, const char *text, int32_t *tsdelta_out) { int32_t tsdelta; + int32_t timestamp_error; /* Not fully intialized, skip */ - if (state->last_tsdelta == 0 && timestamp == state->last_timestamp) + if (sstate->last_tsdelta == 0 && timestamp == sstate->last_timestamp) return 0; - if (seq == state->last_seq) { - if (timestamp != state->last_timestamp) { - state->err_ts_counter += 1; + if (seq == sstate->last_seq) { + if (timestamp != sstate->last_timestamp) { + sstate->err_ts_counter += 1; LOGP(DMGCP, LOGL_ERROR, "The %s timestamp delta is != 0 but the sequence " - "number %d is the same" + "number %d is the same, " + "TS offset: %d, SeqNo offset: %d " "on 0x%x SSRC: %u timestamp: %u " "from %s:%d in %d\n", text, seq, - ENDPOINT_NUMBER(endp), state->ssrc, timestamp, + state->timestamp_offset, state->seq_offset, + ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } @@ -164,31 +182,30 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, } tsdelta = - (int32_t)(timestamp - state->last_timestamp) / - (int16_t)(seq - state->last_seq); + (int32_t)(timestamp - sstate->last_timestamp) / + (int16_t)(seq - sstate->last_seq); if (tsdelta == 0) { - state->err_ts_counter += 1; - LOGP(DMGCP, LOGL_ERROR, + /* Don't update *tsdelta_out */ + LOGP(DMGCP, LOGL_NOTICE, "The %s timestamp delta is %d " "on 0x%x SSRC: %u timestamp: %u " "from %s:%d in %d\n", text, tsdelta, - ENDPOINT_NUMBER(endp), state->ssrc, timestamp, + ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); return 0; } - if (state->last_tsdelta != tsdelta) { - if (state->last_tsdelta) { - state->err_ts_counter += 1; - LOGP(DMGCP, LOGL_ERROR, + if (sstate->last_tsdelta != tsdelta) { + if (sstate->last_tsdelta) { + LOGP(DMGCP, LOGL_INFO, "The %s timestamp delta changes from %d to %d " "on 0x%x SSRC: %u timestamp: %u from %s:%d in %d\n", - text, state->last_tsdelta, tsdelta, - ENDPOINT_NUMBER(endp), state->ssrc, timestamp, + text, sstate->last_tsdelta, tsdelta, + ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } @@ -197,6 +214,25 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, if (tsdelta_out) *tsdelta_out = tsdelta; + timestamp_error = + compute_timestamp_aligment_error(sstate, state->packet_duration, + timestamp); + + if (timestamp_error) { + sstate->err_ts_counter += 1; + LOGP(DMGCP, LOGL_NOTICE, + "The %s timestamp has an alignment error of %d " + "on 0x%x SSRC: %u " + "SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d " + "from %s:%d in %d\n", + text, timestamp_error, + ENDPOINT_NUMBER(endp), sstate->ssrc, + (int16_t)(seq - sstate->last_seq), + (int32_t)(timestamp - sstate->last_timestamp), + tsdelta, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } return 1; } @@ -253,6 +289,42 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, return timestamp_offset; } +/* Set the timestamp offset according to the packet duration. */ +static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, + struct mgcp_rtp_state *state, + struct mgcp_rtp_end *rtp_end, + struct sockaddr_in *addr, + uint32_t timestamp) +{ + int timestamp_error = 0; + int ptime = state->packet_duration; + + /* Align according to: T + Toffs - Tlast = k * Tptime */ + + timestamp_error = compute_timestamp_aligment_error( + &state->out_stream, ptime, + timestamp + state->timestamp_offset); + + if (timestamp_error) { + state->timestamp_offset += ptime - timestamp_error; + + LOGP(DMGCP, LOGL_NOTICE, + "Corrected timestamp alignment error of %d on 0x%x SSRC: %u " + "new TS offset: %d, " + "from %s:%d in %d\n", + timestamp_error, + ENDPOINT_NUMBER(endp), state->in_stream.ssrc, + state->timestamp_offset, inet_ntoa(addr->sin_addr), + ntohs(addr->sin_port), endp->conn_mode); + } + + OSMO_ASSERT(compute_timestamp_aligment_error(&state->out_stream, ptime, + timestamp + state->timestamp_offset) == 0); + + return timestamp_error; +} + + /** * The RFC 3550 Appendix A assumes there are multiple sources but * some of the supported endpoints (e.g. the nanoBTS) can only handle @@ -300,15 +372,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->seq_offset, state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); - if (state->packet_duration == 0 && rtp_end->force_constant_timing) - LOGP(DMGCP, LOGL_ERROR, - "Cannot patch timestamps on 0x%x: " - "RTP packet duration is unknown, SSRC: %u, " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - + if (state->packet_duration == 0) { + state->packet_duration = rtp_end->rate * 20 / 1000; + LOGP(DMGCP, LOGL_NOTICE, + "Fixed packet duration is not available on 0x%x, " + "using fixed 20ms instead: %d from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->packet_duration, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), + endp->conn_mode); + } } else if (state->in_stream.ssrc != ssrc) { LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed on 0x%x: %u -> %u " @@ -346,7 +418,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->in_stream.last_tsdelta = 0; } else { /* Compute current per-packet timestamp delta */ - check_rtp_timestamp(endp, &state->in_stream, rtp_end, addr, + check_rtp_timestamp(endp, state, &state->in_stream, rtp_end, addr, seq, timestamp, "input", &state->in_stream.last_tsdelta); @@ -358,15 +430,10 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta state->in_stream.last_timestamp = timestamp; state->in_stream.last_seq = seq; - if (rtp_end->force_constant_timing && - state->out_stream.ssrc == ssrc && state->packet_duration) { - /* Update the timestamp offset */ - const int delta_seq = - seq + state->seq_offset - state->out_stream.last_seq; - - adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, - delta_seq, timestamp); - } + if (rtp_end->force_aligned_timing && + state->out_stream.ssrc == ssrc && state->packet_duration) + /* Align the timestamp offset */ + align_rtp_timestamp_offset(endp, state, rtp_end, addr, timestamp); /* Store the updated SSRC back to the packet */ if (state->patch_ssrc) @@ -382,8 +449,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta /* Check again, whether the timestamps are still valid */ if (state->out_stream.ssrc == ssrc) - check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr, - seq, timestamp, "output", + check_rtp_timestamp(endp, state, &state->out_stream, rtp_end, + addr, seq, timestamp, "output", &state->out_stream.last_tsdelta); /* diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 7d3ad74..ddec44d 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -700,13 +700,13 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc; - rtp->force_constant_timing = tcfg->force_constant_timing; + rtp->force_aligned_timing = tcfg->force_aligned_timing; rtp->force_constant_ssrc = patch_ssrc ? 1 : 0; LOGP(DMGCP, LOGL_DEBUG, "Configuring RTP endpoint: local port %d%s%s\n", ntohs(rtp->rtp_port), - rtp->force_constant_timing ? ", force constant timing" : "", + rtp->force_aligned_timing ? ", force constant timing" : "", rtp->force_constant_ssrc ? ", force constant ssrc" : ""); } diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 235b8bd..8411b4a 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -89,11 +89,11 @@ static int config_write_mgcp(struct vty *vty) vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); else vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); - if (g_cfg->trunk.force_constant_ssrc || g_cfg->trunk.force_constant_timing) { + if (g_cfg->trunk.force_constant_ssrc || g_cfg->trunk.force_aligned_timing) { vty_out(vty, " %srtp-patch ssrc%s", g_cfg->trunk.force_constant_ssrc ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %srtp-patch timestamp%s", - g_cfg->trunk.force_constant_timing ? "" : "no ", VTY_NEWLINE); + g_cfg->trunk.force_aligned_timing ? "" : "no ", VTY_NEWLINE); } else vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); if (g_cfg->trunk.audio_payload != -1) @@ -480,7 +480,7 @@ DEFUN(cfg_mgcp_patch_rtp_ts, "Adjust RTP timestamp\n" ) { - g_cfg->trunk.force_constant_timing = 1; + g_cfg->trunk.force_aligned_timing = 1; return CMD_SUCCESS; } @@ -491,7 +491,7 @@ DEFUN(cfg_mgcp_no_patch_rtp_ts, "Adjust RTP timestamp\n" ) { - g_cfg->trunk.force_constant_timing = 0; + g_cfg->trunk.force_aligned_timing = 0; return CMD_SUCCESS; } @@ -501,7 +501,7 @@ DEFUN(cfg_mgcp_no_patch_rtp, NO_STR RTP_PATCH_STR) { g_cfg->trunk.force_constant_ssrc = 0; - g_cfg->trunk.force_constant_timing = 0; + g_cfg->trunk.force_aligned_timing = 0; return CMD_SUCCESS; } @@ -598,11 +598,11 @@ static int config_write_trunk(struct vty *vty) vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); else vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); - if (trunk->force_constant_ssrc || trunk->force_constant_timing) { + if (trunk->force_constant_ssrc || trunk->force_aligned_timing) { vty_out(vty, " %srtp-patch ssrc%s", trunk->force_constant_ssrc ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %srtp-patch timestamp%s", - trunk->force_constant_timing ? "" : "no ", VTY_NEWLINE); + trunk->force_aligned_timing ? "" : "no ", VTY_NEWLINE); } else vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); if (trunk->audio_fmtp_extra) @@ -747,7 +747,7 @@ DEFUN(cfg_trunk_patch_rtp_ts, ) { struct mgcp_trunk_config *trunk = vty->index; - trunk->force_constant_timing = 1; + trunk->force_aligned_timing = 1; return CMD_SUCCESS; } @@ -759,7 +759,7 @@ DEFUN(cfg_trunk_no_patch_rtp_ts, ) { struct mgcp_trunk_config *trunk = vty->index; - trunk->force_constant_timing = 0; + trunk->force_aligned_timing = 0; return CMD_SUCCESS; } @@ -770,7 +770,7 @@ DEFUN(cfg_trunk_no_patch_rtp, { struct mgcp_trunk_config *trunk = vty->index; trunk->force_constant_ssrc = 0; - trunk->force_constant_timing = 0; + trunk->force_aligned_timing = 0; return CMD_SUCCESS; } diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 8b1b924..3cfc183 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -660,7 +660,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) trunk.number_endpoints = 1; trunk.endpoints = &endp; trunk.force_constant_ssrc = patch_ssrc; - trunk.force_constant_timing = patch_ts; + trunk.force_aligned_timing = patch_ts; endp.tcfg = &trunk; diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 03c1cf0..638ac92 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -72,21 +72,21 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 320, dTS: 160, Seq: 2 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 320, dTS: 160, Seq: 3 -Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 480, dTS: 160, Seq: 4 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 640, dTS: 160, Seq: 5 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 960, dTS: 320, Seq: 6 -Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 In TS: 1120, dTS: 160, Seq: 7 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1280, dTS: 160, Seq: 8 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1400, dTS: 120, Seq: 9 Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 In TS: 1560, dTS: 160, Seq: 10 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1720, dTS: 160, Seq: 11 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 34688, dTS: 0, Seq: 12 @@ -98,7 +98,7 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35128, dTS: 120, Seq: 15 Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 In TS: 35288, dTS: 160, Seq: 16 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35448, dTS: 160, Seq: 17 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35768, dTS: 160, Seq: 19 @@ -128,21 +128,21 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 320, dTS: 160, Seq: 2 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 320, dTS: 160, Seq: 3 -Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 480, dTS: 160, Seq: 4 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 640, dTS: 160, Seq: 5 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 960, dTS: 320, Seq: 6 -Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 In TS: 1120, dTS: 160, Seq: 7 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1280, dTS: 160, Seq: 8 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1400, dTS: 120, Seq: 9 Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 In TS: 1560, dTS: 160, Seq: 10 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1720, dTS: 160, Seq: 11 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Output SSRC changed to 10203040 @@ -155,7 +155,7 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35128, dTS: 120, Seq: 15 Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 In TS: 35288, dTS: 160, Seq: 16 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35448, dTS: 160, Seq: 17 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35768, dTS: 160, Seq: 19 @@ -185,21 +185,21 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 320, dTS: 160, Seq: 2 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 320, dTS: 160, Seq: 3 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 480, dTS: 160, Seq: 4 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 640, dTS: 160, Seq: 5 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 960, dTS: 320, Seq: 6 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 In TS: 1120, dTS: 160, Seq: 7 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1280, dTS: 160, Seq: 8 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1400, dTS: 120, Seq: 9 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 In TS: 1560, dTS: 160, Seq: 10 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1720, dTS: 160, Seq: 11 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Output SSRC changed to 10203040 @@ -212,7 +212,7 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35128, dTS: 120, Seq: 15 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 In TS: 35288, dTS: 160, Seq: 16 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35448, dTS: 160, Seq: 17 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35768, dTS: 160, Seq: 19 @@ -228,7 +228,7 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 36408, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 36568, dTS: 160, Seq: 23 -Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +1, out +0 +Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 In TS: 36728, dTS: 160, Seq: 24 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 36888, dTS: 160, Seq: 25 @@ -242,21 +242,21 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 320, dTS: 160, Seq: 2 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 320, dTS: 160, Seq: 3 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 480, dTS: 160, Seq: 4 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 640, dTS: 160, Seq: 5 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 960, dTS: 320, Seq: 6 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 In TS: 1120, dTS: 160, Seq: 7 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1280, dTS: 160, Seq: 8 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1400, dTS: 120, Seq: 9 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 In TS: 1560, dTS: 160, Seq: 10 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 1720, dTS: 160, Seq: 11 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 34688, dTS: 0, Seq: 12 @@ -268,7 +268,7 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35128, dTS: 120, Seq: 15 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 In TS: 35288, dTS: 160, Seq: 16 -Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 +Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35448, dTS: 160, Seq: 17 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 35768, dTS: 160, Seq: 19 @@ -284,7 +284,7 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 36408, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 36568, dTS: 160, Seq: 23 -Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +1, out +0 +Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 In TS: 36728, dTS: 160, Seq: 24 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 In TS: 36888, dTS: 160, Seq: 25 -- 1.7.9.5 From holger at freyther.de Thu Dec 19 11:49:03 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Thu, 19 Dec 2013 12:49:03 +0100 Subject: [PATCH 3/3] mgcp/rtp: Only patch timestamp alignment errors In-Reply-To: <1387367691-17503-3-git-send-email-jerlbeck@sysmocom.de> References: <1387367691-17503-1-git-send-email-jerlbeck@sysmocom.de> <1387367691-17503-3-git-send-email-jerlbeck@sysmocom.de> Message-ID: <20131219114903.GA20881@xiaoyu.lan> On Wed, Dec 18, 2013 at 12:54:51PM +0100, Jacob Erlbeck wrote: > +static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, The caller do not check the return value and there is a side-effect to state. I would change it to void. Same goes for align_rtp_timestamp_offset. > @@ -72,21 +72,21 @@ Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 > In TS: 320, dTS: 160, Seq: 2 > Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 > In TS: 320, dTS: 160, Seq: 3 > -Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +1, out +1 > +Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 What do you think about including the absolute time of the output as well? The other thing that I think is not tested with the data is re-ordering of packages? kind regards holger From holger at freyther.de Fri Dec 27 14:59:03 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Fri, 27 Dec 2013 15:59:03 +0100 Subject: Making jolly/testing ready to be merged? Message-ID: <20131227145903.GA20567@xiaoyu.lan> Dear Andreas, going through the patchset of jolly/testing in OpenBSC it would be nice to get most of the things merged (having TCH/H support for something like the 31C3 is a nice goal). I wanted to start with merging the Connection Failure Criteria code but I am confused about the conversion. When parsing the criteria value from from the VTY you divide by four and subtract (so 4 becomes 0). You ondo this when saving the config and patching it in the nanoBTS attribute array. Wouldn't it be more easy to not doing the conversion? holger From holger at freyther.de Mon Dec 30 09:55:34 2013 From: holger at freyther.de (Holger Hans Peter Freyther) Date: Mon, 30 Dec 2013 10:55:34 +0100 Subject: RFC adding another debug log statement Message-ID: <20131230095534.GE22160@xiaoyu.lan> Good Morning, during the 30C3 we had some "broken channels" because the BTS didn't respond within four seconds to a Channel Activation/Deactivation. I started to look at it with perf and "snprintf" appears to be called a lot and did show up in the top. snprintf is mostly called from within osmo_hexdump and gsm_lchan_name. Both of these function calls are used in our logging calls throughout the code. Going forward I think we can do * Remove some LOGP/DEBUG with osmo_hexdump (I did so for some call sites in the osmo-bts) * Start handlin -DNDEBUG inside logging.h and undefine/not define 'DEBUG' in logging.h. But this would take away some code paths that would be nice to look at. * gsm_lchan_name could 'cache' the last lchan and then not call snprintf. I did this once but it didn't appear to be much help. I will have another look. * Introduce a DEBUGQ, 'Q' for quick and check if the output would be enabled and then log. This can be useful when the cost of checking all targets twice is smaller than the cost of snprintf. comments? holger diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h index 1d57e22..a8476d2 100644 --- a/include/osmocom/core/logging.h +++ b/include/osmocom/core/logging.h @@ -27,6 +27,10 @@ #define DEBUGPC(ss, fmt, args...) #endif +#define DEBUGQ(ss, fmt, args) \ + if (log_would_log(ss, LOGL_DEBUG)) \ + logp(ss, __FILE__, __LINE__, 0, fmt ## args); + void osmo_vlogp(int subsys, int level, const char *file, int line, int cont, const char *format, va_list ap); @@ -215,6 +219,9 @@ const char *log_vty_command_description(const struct log_info *info); struct log_target *log_target_find(int type, const char *fname); extern struct llist_head osmo_log_target_list; +/* helper to check if it would log without using filter_fn */ +int log_would_log(int subsys, unsigned int level); + /*! @} */ #endif /* _OSMOCORE_LOGGING_H */ diff --git a/src/logging.c b/src/logging.c index 2e3a80a..1e64ba5 100644 --- a/src/logging.c +++ b/src/logging.c @@ -268,6 +268,26 @@ err: target->output(target, level, buf); } + +static int cat_enabled(const struct log_target *tar, + const struct log_category *category, int level) +{ + /* subsystem is not supposed to be logged */ + if (!category->enabled) + return 0; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + return 0; + + /* Check the category log level */ + if (tar->loglevel == 0 && category->loglevel != 0 && + level < category->loglevel) + return 0; + + return 1; +} + /*! \brief vararg version of logging function */ void osmo_vlogp(int subsys, int level, const char *file, int line, int cont, const char *format, va_list ap) @@ -286,17 +306,8 @@ void osmo_vlogp(int subsys, int level, const char *file, int line, va_list bp; category = &tar->categories[subsys]; - /* subsystem is not supposed to be logged */ - if (!category->enabled) - continue; - /* Check the global log level */ - if (tar->loglevel != 0 && level < tar->loglevel) - continue; - - /* Check the category log level */ - if (tar->loglevel == 0 && category->loglevel != 0 && - level < category->loglevel) + if (!cat_enabled(tar, category, level)) continue; /* Apply filters here... if that becomes messy we will @@ -799,4 +810,19 @@ int log_init(const struct log_info *inf, void *ctx) return 0; } +int log_would_log(int subsys, unsigned int level) +{ + struct log_target *tar; + + llist_for_each_entry(tar, &osmo_log_target_list, entry) { + if (!cat_enabled(tar, &tar->categories[subsys], level)) + continue; + + if ((tar->filter_map & LOG_FILTER_ALL) != 0) + return 1; + } + + return 0; +} + /*! @} */ From noselasd at fiane.dyndns.org Tue Dec 31 02:57:40 2013 From: noselasd at fiane.dyndns.org (noselasd at fiane.dyndns.org) Date: Tue, 31 Dec 2013 03:57:40 +0100 Subject: RFC adding another debug log statement In-Reply-To: <20131230095534.GE22160@xiaoyu.lan> References: <20131230095534.GE22160@xiaoyu.lan> Message-ID: <52C232A4.4030100@fiane.dyndns.org> On 12/30/2013 10:55 AM, Holger Hans Peter Freyther wrote: > Good Morning, > > during the 30C3 we had some "broken channels" because the BTS didn't > respond within four seconds to a Channel Activation/Deactivation. I > started to look at it with perf and "snprintf" appears to be called a > lot and did show up in the top. > > snprintf is mostly called from within osmo_hexdump and gsm_lchan_name. > Both of these function calls are used in our logging calls throughout > the code. Would it be worth it to get rid of snprintf in osmo_hexdump ? Anecdotally I've converted similar code away from snprintf to a more raw variant resulting in a 7-8 times speed increase of the program whose main purpose was to hex encode messages. see attached patch for an example (it may now truncate the hex dumps at a byte boundary vs at a nibble when using snprintf if the hex exceeds sizeof(hexd_buff) though) -------------- next part -------------- A non-text attachment was scrubbed... Name: libosmocore_faster_hexdump.patch Type: text/x-patch Size: 996 bytes Desc: not available URL: From andreas at eversberg.eu Thu Dec 5 13:37:11 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Thu, 5 Dec 2013 14:37:11 +0100 Subject: [PATCH 1/9] Add function to update TRAU muxer after assignment or handover Message-ID: E1 based BTS use TRAU muxer to decode TRAU frames. After changing channel from one timeslot to another (due to handover or assignment), the TRAU muxer must be updated. The call reference of the call is disconnected from the old channel and connected to the new channel. --- openbsc/include/openbsc/gsm_data.h | 14 ++++++++++++++ openbsc/include/openbsc/trau_mux.h | 3 +++ openbsc/src/libbsc/bsc_api.c | 5 +++++ openbsc/src/libbsc/handover_logic.c | 7 +++++-- openbsc/src/libmsc/gsm_04_08.c | 15 ++++++++++++--- openbsc/src/libtrau/trau_mux.c | 18 ++++++++++++++++++ 6 files changed, 57 insertions(+), 5 deletions(-) diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 7c3ca84..41fe328 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -382,6 +382,20 @@ static inline int is_nokia_bts(struct gsm_bts *bts) return 0; } +static inline int is_e1_bts(struct gsm_bts *bts) +{ + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_RBS2000: + case GSM_BTS_TYPE_NOKIA_SITE: + return 1; + default: + break; + } + + return 0; +} + enum gsm_auth_policy gsm_auth_policy_parse(const char *arg); const char *gsm_auth_policy_name(enum gsm_auth_policy policy); diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h index 3de50f7..d211d8d 100644 --- a/openbsc/include/openbsc/trau_mux.h +++ b/openbsc/include/openbsc/trau_mux.h @@ -51,6 +51,9 @@ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref); /* send trau from application */ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame); +/* switch trau muxer to new lchan */ +int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan); + /* callback invoked if we receive TRAU frames */ int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c index 86d2493..e567038 100644 --- a/openbsc/src/libbsc/bsc_api.c +++ b/openbsc/src/libbsc/bsc_api.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -419,6 +420,10 @@ static void handle_ass_compl(struct gsm_subscriber_connection *conn, return; } + /* switch TRAU muxer for E1 based BTS from one channel to another */ + if (is_e1_bts(conn->bts)) + switch_trau_mux(conn->lchan, conn->secondary_lchan); + /* swap channels */ osmo_timer_del(&conn->T10); diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c index 9cf26af..36a758b 100644 --- a/openbsc/src/libbsc/handover_logic.c +++ b/openbsc/src/libbsc/handover_logic.c @@ -39,6 +39,7 @@ #include #include #include +#include struct bsc_handover { struct llist_head list; @@ -264,6 +265,10 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) osmo_timer_del(&ho->T3103); + /* switch TRAU muxer for E1 based BTS from one channel to another */ + if (is_e1_bts(new_lchan->conn->bts)) + switch_trau_mux(ho->old_lchan, new_lchan); + /* Replace the ho lchan with the primary one */ if (ho->old_lchan != new_lchan->conn->lchan) LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); @@ -278,8 +283,6 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); - /* do something to re-route the actual speech frames ! */ - llist_del(&ho->list); talloc_free(ho); diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 0c6b100..b7af7d2 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -1648,6 +1648,9 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) lchan = trans->conn->lchan; bts = lchan->ts->trx->bts; + /* store receive state */ + trans->tch_recv = enable; + switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: @@ -1655,10 +1658,9 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n"); return -EINVAL; } - /* in case, we don't have a RTP socket yet, we note this - * in the transaction and try later */ + /* In case, we don't have a RTP socket to the BTS yet, we try + * later when the socket has been created and connected. */ if (!lchan->abis_ip.rtp_socket) { - trans->tch_recv = enable; DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); return 0; } @@ -1677,6 +1679,13 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_RBS2000: case GSM_BTS_TYPE_NOKIA_SITE: + /* In case we don't have a TCH with correct mode, we try later + * after assignment or handover is complete. This is performed + * by switch_trau_mux() then. */ + if (lchan->tch_mode == GSM48_CMODE_SIGN) { + DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); + return 0; + } if (enable) return trau_recv_lchan(lchan, callref); return trau_mux_unmap(NULL, callref); diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c index c9d77cf..7b9bac0 100644 --- a/openbsc/src/libtrau/trau_mux.c +++ b/openbsc/src/libtrau/trau_mux.c @@ -31,6 +31,7 @@ #include #include #include +#include /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ @@ -518,3 +519,20 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, TRAU_FRAME_BITS); } + +/* switch trau muxer to new lchan */ +int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan) +{ + struct gsm_network *net = old_lchan->ts->trx->bts->network; + struct gsm_trans *trans; + + /* look up transaction with TCH frame receive enabled */ + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->conn && trans->conn->lchan == old_lchan && trans->tch_recv) { + /* switch */ + trau_recv_lchan(new_lchan, trans->callref); + } + } + + return 0; +} -- 1.8.1.5 --------------090508000200070804000108-- From andreas at eversberg.eu Thu Dec 5 13:37:11 2013 From: andreas at eversberg.eu (Andreas Eversberg) Date: Thu, 5 Dec 2013 14:37:11 +0100 Subject: [PATCH 1/9] Add function to update TRAU muxer after assignment or handover Message-ID: E1 based BTS use TRAU muxer to decode TRAU frames. After changing channel from one timeslot to another (due to handover or assignment), the TRAU muxer must be updated. The call reference of the call is disconnected from the old channel and connected to the new channel. --- openbsc/include/openbsc/gsm_data.h | 14 ++++++++++++++ openbsc/include/openbsc/trau_mux.h | 3 +++ openbsc/src/libbsc/bsc_api.c | 5 +++++ openbsc/src/libbsc/handover_logic.c | 7 +++++-- openbsc/src/libmsc/gsm_04_08.c | 19 ++++++++++++++++--- openbsc/src/libtrau/trau_mux.c | 18 ++++++++++++++++++ 6 files changed, 61 insertions(+), 5 deletions(-) diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 7c3ca84..41fe328 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -382,6 +382,20 @@ static inline int is_nokia_bts(struct gsm_bts *bts) return 0; } +static inline int is_e1_bts(struct gsm_bts *bts) +{ + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_RBS2000: + case GSM_BTS_TYPE_NOKIA_SITE: + return 1; + default: + break; + } + + return 0; +} + enum gsm_auth_policy gsm_auth_policy_parse(const char *arg); const char *gsm_auth_policy_name(enum gsm_auth_policy policy); diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h index 3de50f7..d211d8d 100644 --- a/openbsc/include/openbsc/trau_mux.h +++ b/openbsc/include/openbsc/trau_mux.h @@ -51,6 +51,9 @@ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref); /* send trau from application */ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame); +/* switch trau muxer to new lchan */ +int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan); + /* callback invoked if we receive TRAU frames */ int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c index 86d2493..e567038 100644 --- a/openbsc/src/libbsc/bsc_api.c +++ b/openbsc/src/libbsc/bsc_api.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -419,6 +420,10 @@ static void handle_ass_compl(struct gsm_subscriber_connection *conn, return; } + /* switch TRAU muxer for E1 based BTS from one channel to another */ + if (is_e1_bts(conn->bts)) + switch_trau_mux(conn->lchan, conn->secondary_lchan); + /* swap channels */ osmo_timer_del(&conn->T10); diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c index 9cf26af..36a758b 100644 --- a/openbsc/src/libbsc/handover_logic.c +++ b/openbsc/src/libbsc/handover_logic.c @@ -39,6 +39,7 @@ #include #include #include +#include struct bsc_handover { struct llist_head list; @@ -264,6 +265,10 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) osmo_timer_del(&ho->T3103); + /* switch TRAU muxer for E1 based BTS from one channel to another */ + if (is_e1_bts(new_lchan->conn->bts)) + switch_trau_mux(ho->old_lchan, new_lchan); + /* Replace the ho lchan with the primary one */ if (ho->old_lchan != new_lchan->conn->lchan) LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); @@ -278,8 +283,6 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); - /* do something to re-route the actual speech frames ! */ - llist_del(&ho->list); talloc_free(ho); diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index 0c6b100..bc4a9c3 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -1648,6 +1648,9 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) lchan = trans->conn->lchan; bts = lchan->ts->trx->bts; + /* store receive state */ + trans->tch_recv = enable; + switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: @@ -1655,10 +1658,12 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n"); return -EINVAL; } - /* in case, we don't have a RTP socket yet, we note this - * in the transaction and try later */ + /* In case, we don't have a RTP socket to the BTS yet, the BTS + * will not be connected to our RTP proxy and the socket will + * not be assigned to the application interface. This method + * will be called again, once the audio socket is created and + * connected. */ if (!lchan->abis_ip.rtp_socket) { - trans->tch_recv = enable; DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); return 0; } @@ -1677,6 +1682,14 @@ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_RBS2000: case GSM_BTS_TYPE_NOKIA_SITE: + /* In case we don't have a TCH with correct mode, the TRAU muxer + * will not be asigned to the application interface. This is + * performed by switch_trau_mux() after successful handover or + * assignment. */ + if (lchan->tch_mode == GSM48_CMODE_SIGN) { + DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); + return 0; + } if (enable) return trau_recv_lchan(lchan, callref); return trau_mux_unmap(NULL, callref); diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c index c9d77cf..7b9bac0 100644 --- a/openbsc/src/libtrau/trau_mux.c +++ b/openbsc/src/libtrau/trau_mux.c @@ -31,6 +31,7 @@ #include #include #include +#include /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ @@ -518,3 +519,20 @@ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, TRAU_FRAME_BITS); } + +/* switch trau muxer to new lchan */ +int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan) +{ + struct gsm_network *net = old_lchan->ts->trx->bts->network; + struct gsm_trans *trans; + + /* look up transaction with TCH frame receive enabled */ + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->conn && trans->conn->lchan == old_lchan && trans->tch_recv) { + /* switch */ + trau_recv_lchan(new_lchan, trans->callref); + } + } + + return 0; +} -- 1.8.1.5 --------------080108050203060401030404--