Dear LaF0rge,
I pushed "zecke/features/emulator" to the osmo-pcu repository. All it does is to send a static GPRS Attach for a foreign TLLI. Looking at the communication with Wireshark one will see:
Identity Requests messages.. always with N(U) = 0
and in the SGSN log one can see:
<0012> gprs_llc.c:773 LLC RX: unknown TLLI 0xadf11820, creating LLME on the fly ... <0012> gprs_llc.c:357 LLC TX: unknown TLLI 0xedf11820, creating LLME on the fly
What happens is the following:
When receiving the GPRS Attach we create a LLME for the 'foreign' TLLI, but the look-up will never compare tlli/old_tlli with the foreign one. It will always be localized.
The smallest change is this one:
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c index 8af5367..52727ce 100644 --- a/openbsc/src/gprs/gprs_llc.c +++ b/openbsc/src/gprs/gprs_llc.c @@ -777,9 +777,10 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) (llhp.cmd == GPRS_LLC_XID || llhp.cmd == GPRS_LLC_UI)) { struct gprs_llc_llme *llme; /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ - llme = llme_alloc(msgb_tlli(msg)); + llme = llme_alloc(tlli_foreign2local(msgb_tlli(msg))); LOGP(DLLC, LOGL_DEBUG, "LLC RX: unknown TLLI 0x%08x, " - "creating LLME on the fly\n", msgb_tlli(msg)); + "creating LLME on the fly\n", + tlli_foreign2local(msgb_tlli(msg))); lle = &llme->lle[llhp.sapi]; } else { LOGP(DLLC, LOGL_NOTICE,
(but one could move that into llme_alloc and then it works for RX/TX). After this patch the Identity Request requests will have an increasting N(U) and the tlli in the message will be 0xadf11820. The SGSN will still print:
<0012> gprs_llc.c:142 TLLI 0xadf11820 is foreign, converting to local TLLI 0xedf11820
So this means that for the entire GPRS attach procedure we will use the initial foreign TLLI.... so somehow... the code in the MM handling...
/* Even if there is no P-TMSI allocated, the MS will switch from * foreign TLLI to local TLLI */ ctx->tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL);
/* Inform LLC layer about new TLLI but keep old active */ gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new, GPRS_ALGO_GEA0, NULL);
So this tlli_new does not appear to be used at all and I don't see how/where we would use/create the OLD_TLLI IE? Is it implemented?
holger
On Sun, Jul 28, 2013 at 10:01:04PM +0200, Holger Hans Peter Freyther wrote:
/* Even if there is no P-TMSI allocated, the MS will switch from * foreign TLLI to local TLLI */ ctx->tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL); /* Inform LLC layer about new TLLI but keep old active */ gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new, GPRS_ALGO_GEA0, NULL);So this tlli_new does not appear to be used at all and I don't see how/where we would use/create the OLD_TLLI IE? Is it implemented?
Okay, this appears to be incomplete. We would need to put dup.tlli in the gprs_llc.c code if we want to send a TLLI (but then the PCU doesn't appear to be bale to look-up the old tbf based on the IE_TLLI).
We do have:
struct sgsn_mm_ctx { tlli; tlli_new; struct gprs_llc_llme { tlli; old_tlli; } }
1.) During the attach msgb_tlli(msg) will be the foriegn tlli 2.) We will generate a tlli_new based on the reported tmsi... 3.) We will llgm_assign the tlli_new and tlli. 4.) msgb_tlli(msg) will still point to the foreign tlli 5.) We send a Identity Request 6.) gprs_llc will not provide a dup.tlli 7.) bssgp_tx_dl_ud will use msgb_tlli(msg) and not put a IE_TLLI
Do you have some documentation/brain-dump on how tlli/tlli_new, tlli and old_tlli interact with each other? E.g. at which point should the tlli_new be used?
In terms of a patch (which is likely to break the PCU as it does not support the old tlli):
1.) Use tlli_foreign2local in the look-up. 2.) Sent the tlli_old if it is not UNASSIGNED and if it is different to the msgb_tlli(msg). I would also like to assert that msgb_tlli(msg) is not equal to mmctx->llme->tlli/old_tlli. 3.) Use the newly assigned tlli as the default.
I would like to apply the last hunk first as it is solving creating a LLME on the fly on the TX path and instead re-uses the one created when receiving the first message.
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c index f7a5cde..eaf9ecd 100644 --- a/openbsc/src/gprs/gprs_gmm.c +++ b/openbsc/src/gprs/gprs_gmm.c @@ -760,6 +760,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, /* Inform LLC layer about new TLLI but keep old active */ gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new, GPRS_ALGO_GEA0, NULL); + ctx->tlli = ctx->llme->tlli;
DEBUGPC(DMM, "\n"); return gsm48_gmm_authorize(ctx, GMM_T3350_MODE_ATT); @@ -989,6 +990,7 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, /* Inform LLC layer about new TLLI but keep old active */ gprs_llgmm_assign(mmctx->llme, mmctx->tlli, mmctx->tlli_new, GPRS_ALGO_GEA0, NULL); + mmctx->tlli = mmctx->llme->tlli;
/* Look at PDP Context Status IE and see if MS's view of * activated/deactivated NSAPIs agrees with our view */ diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c index 8af5367..ee99d16 100644 --- a/openbsc/src/gprs/gprs_llc.c +++ b/openbsc/src/gprs/gprs_llc.c @@ -48,6 +48,9 @@ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) * not yet have a MMC context (e.g. XID negotiation of primarly * LLC connection fro GMM sapi). */ if (mmctx) { + if (msgb_tlli(msg) != mmctx->llme->old_tlli + && mmctx->llme->old_tlli != 0xffffffff) + dup.tlli = &mmctx->llme->old_tlli; dup.imsi = mmctx->imsi; dup.drx_parms = mmctx->drx_parms; dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; @@ -154,7 +157,7 @@ static struct gprs_llc_lle *lle_by_tlli_sapi(uint32_t tlli, uint8_t sapi) tlli = tlli_foreign2local(tlli);
llist_for_each_entry(llme, &gprs_llc_llmes, list) { - if (llme->tlli == tlli || llme->old_tlli == tlli) + if (llme->tlli == tlli || tlli_foreign2local(llme->old_tlli) == tlli) return &llme->lle[sapi]; } return NULL;
Hi Holger,
thanks a lot for debugging.
On Sun, Jul 28, 2013 at 10:54:08PM +0200, Holger Hans Peter Freyther wrote:
On Sun, Jul 28, 2013 at 10:01:04PM +0200, Holger Hans Peter Freyther wrote:
/* Even if there is no P-TMSI allocated, the MS will switch from * foreign TLLI to local TLLI */ ctx->tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL); /* Inform LLC layer about new TLLI but keep old active */ gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new, GPRS_ALGO_GEA0, NULL);So this tlli_new does not appear to be used at all and I don't see how/where we would use/create the OLD_TLLI IE? Is it implemented?
It seems the BSSGP TLLI (old) is not implemented at this point, as dup->tlli is not set, as you indicated.
Okay, this appears to be incomplete. We would need to put dup.tlli in the gprs_llc.c code if we want to send a TLLI (but then the PCU doesn't appear to be bale to look-up the old tbf based on the IE_TLLI).
dup->tlli should be set to the old TLLI, _if_ there has just been a change of TLLI. So basically at the time the gprs_llgmm_assign(old_tmsi=0xffffffff, new_tmsi=....) is happening when we get the RAU_COMPLETE / ATTACH_COMPLETE in GMM. At that point, the next BSSGP_DL_UD should have the "TLLI (old)" IE. Only at that point we can be sure that any further messages from the MS will contain only the new TLLI, which is indicated by the BSSGP spec:
If an SGSN provides a second TLLI, indicating that an MS has recently changed its TLLI, this shall be considered as the "old" TLLI. A BSS uses the "old" TLLI to locate an MS's existing context. Subsequent uplink data transfers for this MS shall reference the current TLLI, and not the old TLLI.
What strikes me as odd is that after we have received such a RAU_COMPLETE / ATTACH_COMPLETE, the SGSN is not sending any further GMM messages to the MS. So the BSSGP-DL-UD with the old TLLI is not sent until maybe a long time later, when the SGSN has something to transmit again. Meanwhile, the MS ca happily be sending uplink messages with the new TLLI.
I'm not really sure if a PCU really needs this, at least not as long as we always include the IMSI as part of BSSGP Downlink Unitdata. At that point, the PCU can look-up its MS-contaxt based on the IMSI. The spec says:
The SGSN shall include the IMSI in the PDU. As an exception, the SGSN may omit the IMSI in the PDU if the mobile station identified by the TLLI is in MM non-DRX mode period (i.e. during a GMM procedure for GPRS attach or routing area updating defined in GSM 24.008 [11]) and the SGSN does not have a valid IMSI.
So this would be the case if we get an ATTACH_REQ or RAU_REQ with P-TMSI only, but have not yet sent the IDENTITY REQUEST or not received the IDENTITY RESPONSE yet. At that point, we don't issue any llgmm_assign() requests to LLC yet, and thus are guaranteed that we always know the IMSI at the time we have a TLLI change.
Do you agree?
Do you have some documentation/brain-dump on how tlli/tlli_new, tlli and old_tlli interact with each other? E.g. at which point should the tlli_new be used?
The tlli_new in the mm-context is only used to call the gprs_llgmm_assign() function (resembling 1:1 a LLGMM-ASSIGN.req primitive in 04.64). So in theory, we should not need to keep it in the mmcontext, but I think it's better to keep it, if not only for debugging aid ;)
The higher layers of the SGSN (GMM, etc.) don't need the tlli_new for any other reason. Basically, at time of receiving/processing the ATTACH or the RA UPDATE, we
a) generate the new (always local, ptmsi derived) TLLI and store it in mmctx->tlli_new. b) issue a LLGMM-ASSIGN.req primitive to LLC, where tlli_old = mmctx->tlli and tlli_new = tlli_new. This is the following clause in the spec:
If TLLI Old ≠ all 1's and TLLI New ≠ all 1's then TLLI Old and TLLI New are assigned, and TLLI New shall be used when (re-)transmitting LLC frames. Both TLLI Old and TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
Later, when we get the ATTACH_COMPLETE / RAU_COMPLETE from the MS, we
a) set mmctx->tlli = mmctx->tlli_new b) issue a LLGMM-ASSIGN.req primitive to LLC with old TMSI 0xffffffff and new TMSI = mmctx->tlli_new. According to spec, this is the following case:
If TLLI Old = all 1's and TLLI New ≠ all 1's then TLLI New shall be assigned and used when (re-)transmitting LLC frames. If a TLLI Old ≠ all 1's was assigned to the LLME, then TLLI Old is unassigned. Only TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
If TLLI Old = all 1's was assigned to the LLME, then this shall be treated as a TLLI assignment according to subclause 8.3.1, and the LLGMM-ASSIGN-REQ shall be the first primitive sent by GMM in order to enable LLC to process requests from layer 3.
So basically, the _intended_ functionality is to accept both old and new TLLI for the time until we receive the ATTACH_COMPLETE / RAU_COMPLETE. After that point, the MS is permitted to only use the new TLLI.
If we do a P-TMSI allocation (which we do as PTMSI_ALLOC is #defined), then the new TLLI will be completely different (LOCAL, derived from new P-TMSI) than the old one (LOCAL or foreign, based on old P-TMSI).
If you disable PTMSI_ALLOC, then the new TLLI will just be the local version of the intiially (potentially) foreign TLLI.
The foreign/local handling in gprs_llc is different from the spec as it predates my full undestanding of specified the TLLI handling. So when we look-up a LLME based on TLLI, we _always_ convert from foreign2local during a lookup, which is not according to spec. At the time window during which the foreign _and_ local TLLI shall be accepted, old_tlli should be the foreign one and tlli should be the (new) local one, i.e. the lookup works even without any foreign2local conversion of the lookup key.
1.) During the attach msgb_tlli(msg) will be the foriegn tlli 2.) We will generate a tlli_new based on the reported tmsi... 3.) We will llgm_assign the tlli_new and tlli. 4.) msgb_tlli(msg) will still point to the foreign tlli 5.) We send a Identity Request 6.) gprs_llc will not provide a dup.tlli 7.) bssgp_tx_dl_ud will use msgb_tlli(msg) and not put a IE_TLLI
Which I so far assume to be what the code should do. The Identity request should still use the foreign TLLI, as we didn't send a ATTACH ACCEPT, ATTACH REJECT, RAU ACCEPT or RAU REJECT yet.
The MS will continue to use the foreign TLLI until those messages are recieved:
Quote from 04.08 4.7.1.4. i)
If the MS has stored a valid P-TMSI, the MS shall derive a foreign TLLI from that P-TMSI and shall use it for transmission of the: - ATTACH REQUEST message of any GPRS combined/non-combined attach procedure; other GMM messages sent during this procedure shall be transmitted using the same foreign TLLI until the ATTACH ACCEPT message or the ATTACH REJECT message is received; and - ROUTING AREA UPDATE REQUEST message of a combined/non-combined RAU procedure if the MS has entered a new routing area, or if the GPRS update status is not equal to GU1 UPDATED. Other GMM messages sent during this procedure shall be transmitted using the same foreign TLLI, until the ROUTING AREA UPDATE ACCEPT message or the ROUTING AREA UPDATE REJECT message is received.
After a successful GPRS attach or routing area update procedure, independent whether a new P-TMSI is assigned, if the MS has stored a valid P-TMSI then the MS shall derive a local TLLI from the stored P-TMSI and shall use it for addressing at lower layers.
So the question is: What should be done for downlink messages at this point.
"After receipt of the acknowledgement, the network shall use the local TLLI for addressing at lower layers.
So during this time, using the msgb_tlli(msg) [which is the old, foreign TLLI received from the MS] as the TLLI in downlink messages like IDENTITY REQUEST seems 100% correct to me.
In terms of a patch (which is likely to break the PCU as it does not support the old tlli):
1.) Use tlli_foreign2local in the look-up.
This would continue to use the current logic, which is more liberal than the spec [see above]. A better approach might be to remove the tlli_foreign2local() in the lle_by_tlli_sapi() function. This way we ensure that
a) the foreign TLLI is matched because it is the msgb_tlli() at the time the LLME was allocated
b) the old and new TLLI is matched after the first gprs_llgmm_assign() call.
c) only the new TLLI is matched after the second gprs_llgmm_assign() call at RAU/ATTACH COMPLETE time
IIRC, the foreign2local logic was introduced by me before we had a gprs_llgmm_assign() primitive and before I fully understood the spec in this part. It should be removed.
2.) Sent the tlli_old if it is not UNASSIGNED and if it is different to the msgb_tlli(msg).
see above, I'm not quite sure if that would work. Irrespective of BSSGP spec compliance with non-osmo PCUs, in osmo-pcu we can always look-up by IMSI.
I would also like to assert that msgb_tlli(msg) is not equal to mmctx->llme->tlli/old_tlli.
makes sense, particularly once we remove the foreign2local logic.
3.) Use the newly assigned tlli as the default.
from which point on? Only _after_ the RAU_COMPLETE / ATTACH_COMPLETE, which I believe we already do now.
Regards, Harald
On Mon, Jul 29, 2013 at 10:19:26AM +0800, Harald Welte wrote:
dup->tlli should be set to the old TLLI, _if_ there has just been a change of TLLI. So basically at the time the gprs_llgmm_assign(old_tmsi=0xffffffff, new_tmsi=....) is happening when we get the RAU_COMPLETE / ATTACH_COMPLETE in GMM. At that point, the next BSSGP_DL_UD should have the "TLLI (old)" IE. Only at that point we can be sure that any further messages from the MS will contain only the new TLLI, which is indicated by the BSSGP spec:
Okay. That does make sense. Thanks for going through the specs.
I'm not really sure if a PCU really needs this, at least not as long as we always include the IMSI as part of BSSGP Downlink Unitdata. At that point, the PCU can look-up its MS-contaxt based on the IMSI. The spec says:
The osmo-pcu is broken in this regard. It will only use the TLLI to identify the temporary block flow (tbf). I will shuffle the code around to resolve that limitation and create unit tests for that.
So this would be the case if we get an ATTACH_REQ or RAU_REQ with P-TMSI only, but have not yet sent the IDENTITY REQUEST or not received the IDENTITY RESPONSE yet. At that point, we don't issue any llgmm_assign() requests to LLC yet, and thus are guaranteed that we always know the IMSI at the time we have a TLLI change.
Do you agree?
Yes, but we do issue llgm_assign in that case (and that is the case I can simulate right now). I think the llgm_assign should just be for the foriegn tlli we received (and not from the one dervied by the P-TMSI that we didn't allocate).
The tlli_new in the mm-context is only used to call the gprs_llgmm_assign() function (resembling 1:1 a LLGMM-ASSIGN.req primitive in 04.64). So in theory, we should not need to keep it in the mmcontext, but I think it's better to keep it, if not only for debugging aid ;)
okay, this duplication confused me a bit.
The higher layers of the SGSN (GMM, etc.) don't need the tlli_new for any other reason. Basically, at time of receiving/processing the ATTACH or the RA UPDATE, we
If TLLI Old ≠ all 1's and TLLI New ≠ all 1's then TLLI Old and TLLI New are assigned, and TLLI New shall be used when (re-)transmitting LLC frames. Both TLLI Old and TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
but for the above ATTACH case with a unknown P-TMSI/TLLI. We will do the assignment but use TLLI Old for the transmissionof any frame.
Later, when we get the ATTACH_COMPLETE / RAU_COMPLETE from the MS, we
a) set mmctx->tlli = mmctx->tlli_new b) issue a LLGMM-ASSIGN.req primitive to LLC with old TMSI 0xffffffff and new TMSI = mmctx->tlli_new. According to spec, this is the following case:
If TLLI Old = all 1's and TLLI New ≠ all 1's then TLLI New shall be assigned and used when (re-)transmitting LLC frames. If a TLLI Old ≠ all 1's was assigned to the LLME, then TLLI Old is unassigned. Only TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
but that means that llme->old_tlli will be all 1's and that even if we want to.. in gprs_llc we could not set dup.tlli to the previous tlli? So the next download unitdata can not have the IE_TLLI set?
The foreign/local handling in gprs_llc is different from the spec as it predates my full undestanding of specified the TLLI handling. So when we look-up a LLME based on TLLI, we _always_ convert from foreign2local during a lookup, which is not according to spec. At the time window during which the foreign _and_ local TLLI shall be accepted, old_tlli should be the foreign one and tlli should be the (new) local one, i.e. the lookup works even without any foreign2local conversion of the lookup key.
Okay, I will modify the lle_by_tlli_sapi look-up to not use the foreign conversion at all. This would fix my case.. but given the lack of unit tests I don't know what I break. :)
This would continue to use the current logic, which is more liberal than the spec [see above]. A better approach might be to remove the tlli_foreign2local() in the lle_by_tlli_sapi() function. This way we ensure that
okay.
see above, I'm not quite sure if that would work. Irrespective of BSSGP spec compliance with non-osmo PCUs, in osmo-pcu we can always look-up by IMSI.
which we don't[1]. The TBF is only searched by tlli. But that is for another mailinglist..
I would also like to assert that msgb_tlli(msg) is not equal to mmctx->llme->tlli/old_tlli.
makes sense, particularly once we remove the foreign2local logic.
okay.
3.) Use the newly assigned tlli as the default.
from which point on? Only _after_ the RAU_COMPLETE / ATTACH_COMPLETE, which I believe we already do now.
yeah, nothing to be done here.
thanks for the clarifications
holger
[1] http://git.osmocom.org/osmo-pcu/tree/src/gprs_bssgp_pcu.cpp#n151
On Mon, Jul 29, 2013 at 08:26:32AM +0200, Holger Hans Peter Freyther wrote:
Hi,
Okay, I will modify the lle_by_tlli_sapi look-up to not use the foreign conversion at all. This would fix my case.. but given the lack of unit tests I don't know what I break. :)
makes sense, particularly once we remove the foreign2local logic.
okay.
the patches implement the above two. My case with pcu_emu appears to work correctly (N(U) is counting up) and I have not looked into anything beyond that.
If somebody at OHM or somewhere else could/want to apply this to their SGSN installation it would be greatly appreciated.
kind regards holger
On Mon, Jul 29, 2013 at 10:12:12AM +0200, Holger Hans Peter Freyther wrote:
If somebody at OHM or somewhere else could/want to apply this to their SGSN installation it would be greatly appreciated.
Hi,
I was doing a GPRS Attach and PDP Context Activation with one of the sysmocom GSM modules and my E71. Without the patches applied the E71 fails to re-attach after a SGSN/PCU re-start with the same symptom (all our identity requests are sent with N(U) = 0). If you don't mind I will push these two changes to master.
I did this without a GGSN running and ran into the PDP Activcation Timeout... and a warning/backtrace. The below should fix it but I don't have time to verify that tonight.
<000f> sgsn_libgtp.c:265 Received CREATE PDP CTX CONF, cause=-1(unknown 0xffffffff) <000f> sgsn_libgtp.c:269 Create PDP ctx req timed out <000f> gprs_sgsn.c:259 freeing PDP context that still has a libgtp handle attached to it, this shouldn't happen! backtrace() returned 11 addresses /home/ich/install/openbsc/lib/libosmocore.so.4(osmo_generate_backtrace+0x16) [0xb72fba36] ./src/gprs/osmo-sgsn() [0x804d364] ./src/gprs/osmo-sgsn() [0x804f859] /home/ich/install/openbsc/lib/libgtp.so.0(gtp_retrans+0x87) [0xb730de97] ./src/gprs/osmo-sgsn() [0x804f446] /home/ich/install/openbsc/lib/libosmocore.so.4(osmo_timers_update+0xb1) [0xb72f7b91] /home/ich/install/openbsc/lib/libosmocore.so.4(osmo_select_main+0x14b) [0xb72f7e8b] ./src/gprs/osmo-sgsn() [0x804a0d1] /lib/i386-linux-gnu/i686/cmov/libc.so.6(__libc_start_main+0xf5) [0xb70af8f5] ./src/gprs/osmo-sgsn() [0x804a17d]
diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c index f2eb35d..bdab082 100644 --- a/openbsc/src/gprs/sgsn_libgtp.c +++ b/openbsc/src/gprs/sgsn_libgtp.c @@ -294,6 +294,9 @@ reject: pctx->state = PDP_STATE_NONE; if (pdp) pdp_freepdp(pdp); + OSMO_ASSERT(pdp == pctx->lib); + pctx->lib = NULL; + /* Send PDP CTX ACT REJ to MS */ rc = gsm48_tx_gsm_act_pdp_rej(pctx->mm, pctx->ti, reject_cause, 0, NULL);
Hi Holger,
On Mon, Jul 29, 2013 at 10:53:18PM +0200, Holger Hans Peter Freyther wrote:
I was doing a GPRS Attach and PDP Context Activation with one of the sysmocom GSM modules and my E71. Without the patches applied the E71 fails to re-attach after a SGSN/PCU re-start with the same symptom (all our identity requests are sent with N(U) = 0). If you don't mind I will push these two changes to master.
Sure, go ahead. that's what I meant by 'be brave' :)
Thanks.
I did this without a GGSN running and ran into the PDP Activcation Timeout... and a warning/backtrace. The below should fix it but I don't have time to verify that tonight.
This seems to be a bogus warning. We do properly free the pdp library handle, but we don't set pctx->lib to NULL in this path, see the below suggested patch.
On Tue, Jul 30, 2013 at 08:03:49AM +0800, Harald Welte wrote:
This seems to be a bogus warning. We do properly free the pdp library handle, but we don't set pctx->lib to NULL in this path, see the below suggested patch.
The warning is actually right.
gtp.c: if (gsn->cb_conf) gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->cbp);
the NULL is the pdp context here
- OSMO_ASSERT(pctx->lib == pdp);
did fail because of that. I will check and free pctx->lib as well and push it in a couple of minutes.
On Tue, Jul 30, 2013 at 08:03:49AM +0800, Harald Welte wrote:
Sure, go ahead. that's what I meant by 'be brave' :)
The engineer in me.. used git gui blame on the file and I found the commit that added the foreign tlli lookup.
commit f0901f0067e363c0ced6254db1b45a9771640412 Author: Harald Welte laforge@gnumonks.org Date: Sun Dec 26 10:39:26 2010 +0100
[SGSN] Fix processing of RA Update Request regarding TLLI
In case we get a RA UPD REQ on a new cell (both served by the same SGSN), the LLC stack should not allocate a ne LLE/LLME, as the latter would reset the V(u)sent / V(u)recv to zero and make the MS discard our responses.
Instead, whenever the LLC stack sees a foreign TLLI, it should always convert it to the local TLLI before doing any lookup for a LLE/LLME.
I had to move some code around but what I essentialy do is.
1.) I do the lookup based on the tlli... 2.) if the tlli is foreign.. I convert it and make another lookup 3.) I create the LLME on the fly or return NULL
Or shall we do this... "oh we know this subscriber already" at a higher level in GMM? E.g. notice that it is a routing area update... and then free the new llme and do a tlli assignment for the foreign tlli?
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c index c3bd9d2..b637acb 100644 --- a/openbsc/src/gprs/gprs_llc.c +++ b/openbsc/src/gprs/gprs_llc.c @@ -36,6 +36,54 @@ #include <openbsc/crc24.h> #include <openbsc/sgsn.h>
+enum gprs_llc_cmd { + GPRS_LLC_NULL, + GPRS_LLC_RR, + GPRS_LLC_ACK, + GPRS_LLC_RNR, + GPRS_LLC_SACK, + GPRS_LLC_DM, + GPRS_LLC_DISC, + GPRS_LLC_UA, + GPRS_LLC_SABM, + GPRS_LLC_FRMR, + GPRS_LLC_XID, + GPRS_LLC_UI, +}; + +static const struct value_string llc_cmd_strs[] = { + { GPRS_LLC_NULL, "NULL" }, + { GPRS_LLC_RR, "RR" }, + { GPRS_LLC_ACK, "ACK" }, + { GPRS_LLC_RNR, "RNR" }, + { GPRS_LLC_SACK, "SACK" }, + { GPRS_LLC_DM, "DM" }, + { GPRS_LLC_DISC, "DISC" }, + { GPRS_LLC_UA, "UA" }, + { GPRS_LLC_SABM, "SABM" }, + { GPRS_LLC_FRMR, "FRMR" }, + { GPRS_LLC_XID, "XID" }, + { GPRS_LLC_UI, "UI" }, + { 0, NULL } +}; + +struct gprs_llc_hdr_parsed { + uint8_t sapi; + uint8_t is_cmd:1, + ack_req:1, + is_encrypted:1; + uint32_t seq_rx; + uint32_t seq_tx; + uint32_t fcs; + uint32_t fcs_calc; + uint8_t *data; + uint16_t data_len; + uint16_t crc_length; + enum gprs_llc_cmd cmd; +}; + +static struct gprs_llc_llme *llme_alloc(uint32_t tlli); + /* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) @@ -162,6 +210,43 @@ static struct gprs_llc_lle *lle_by_tlli_sapi(const uint32_t tlli, uint8_t sapi) return NULL; }
+/* lookup LLC Entity for RX based on DLCI (TLLI+SAPI tuple) */ +static struct gprs_llc_lle *lle_for_rx_by_tlli_sapi(const uint32_t tlli, + uint8_t sapi, enum gprs_llc_cmd cmd) +{ + struct gprs_llc_lle *lle; + + /* We already know about this TLLI */ + lle = lle_by_tlli_sapi(tlli, sapi); + if (lle) + return lle; + + /* Maybe it is a routing area update but we already know this sapi? */ + if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { + lle = lle_by_tlli_sapi(tlli_foreign2local(tlli), sapi); + if (lle) + return lle; + } + + /* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded, + * except UID and XID frames with SAPI=1 */ + if (sapi == GPRS_SAPI_GMM && + (cmd == GPRS_LLC_XID || cmd == GPRS_LLC_UI)) { + struct gprs_llc_llme *llme; + /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ + llme = llme_alloc(tlli); + LOGP(DLLC, LOGL_DEBUG, "LLC RX: unknown TLLI 0x%08x, " + "creating LLME on the fly\n", tlli); + lle = &llme->lle[sapi]; + return lle; + } + + LOGP(DLLC, LOGL_NOTICE, + "unknown TLLI(0x%08x)/SAPI(%d): Silently dropping\n", + tlli, sapi); + return NULL; +} + static void lle_init(struct gprs_llc_llme *llme, uint8_t sapi) { struct gprs_llc_lle *lle = &llme->lle[sapi]; @@ -201,52 +286,6 @@ static void llme_free(struct gprs_llc_llme *llme) talloc_free(llme); }
-enum gprs_llc_cmd { - GPRS_LLC_NULL, - GPRS_LLC_RR, - GPRS_LLC_ACK, - GPRS_LLC_RNR, - GPRS_LLC_SACK, - GPRS_LLC_DM, - GPRS_LLC_DISC, - GPRS_LLC_UA, - GPRS_LLC_SABM, - GPRS_LLC_FRMR, - GPRS_LLC_XID, - GPRS_LLC_UI, -}; - -static const struct value_string llc_cmd_strs[] = { - { GPRS_LLC_NULL, "NULL" }, - { GPRS_LLC_RR, "RR" }, - { GPRS_LLC_ACK, "ACK" }, - { GPRS_LLC_RNR, "RNR" }, - { GPRS_LLC_SACK, "SACK" }, - { GPRS_LLC_DM, "DM" }, - { GPRS_LLC_DISC, "DISC" }, - { GPRS_LLC_UA, "UA" }, - { GPRS_LLC_SABM, "SABM" }, - { GPRS_LLC_FRMR, "FRMR" }, - { GPRS_LLC_XID, "XID" }, - { GPRS_LLC_UI, "UI" }, - { 0, NULL } -}; - -struct gprs_llc_hdr_parsed { - uint8_t sapi; - uint8_t is_cmd:1, - ack_req:1, - is_encrypted:1; - uint32_t seq_rx; - uint32_t seq_tx; - uint32_t fcs; - uint32_t fcs_calc; - uint8_t *data; - uint16_t data_len; - uint16_t crc_length; - enum gprs_llc_cmd cmd; -}; - #define LLC_ALLOC_SIZE 16384 #define UI_HDR_LEN 3 #define N202 4 @@ -770,25 +809,9 @@ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) }
/* find the LLC Entity for this TLLI+SAPI tuple */ - lle = lle_by_tlli_sapi(msgb_tlli(msg), llhp.sapi); - - /* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded, - * except UID and XID frames with SAPI=1 */ - if (!lle) { - if (llhp.sapi == GPRS_SAPI_GMM && - (llhp.cmd == GPRS_LLC_XID || llhp.cmd == GPRS_LLC_UI)) { - struct gprs_llc_llme *llme; - /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ - llme = llme_alloc(msgb_tlli(msg)); - LOGP(DLLC, LOGL_DEBUG, "LLC RX: unknown TLLI 0x%08x, " - "creating LLME on the fly\n", msgb_tlli(msg)); - lle = &llme->lle[llhp.sapi]; - } else { - LOGP(DLLC, LOGL_NOTICE, - "unknown TLLI/SAPI: Silently dropping\n"); - return 0; - } - } + lle = lle_for_rx_by_tlli_sapi(msgb_tlli(msg), llhp.sapi, llhp.cmd); + if (!lle) + return 0;
/* decrypt information field + FCS, if needed! */ if (llhp.is_encrypted) {
On Tue, Jul 30, 2013 at 09:20:38AM +0200, Holger Hans Peter Freyther wrote:
Or shall we do this... "oh we know this subscriber already" at a higher level in GMM? E.g. notice that it is a routing area update... and then free the new llme and do a tlli assignment for the foreign tlli?
The patch is not enough.. I ran into the newly added ASSERT about the TLLI.
1.) E71 attach to a network and create a PDP context.. 2.) Restart SGSN/GGSN 3.) Phone tries Activate/Deactivate PDP Context.. we send a GMM status with impl. detached.. E71 sends a SM Status for protocol error 4.) Re-open the browser app... it tries GPRS Attach for a foreign tlli (based on the old one).. we find it.. the state doesn't match so there is re-transmission going on.. once we have the righ seq. number the RX will find the 'right' LLME.. but on TX for the foreign TLLI we create a LLME on the fly and run into the assert...
I can now extend the second phase of look-ups and soften the assert a bit but I do wonder if that is the correct thing to do.
I added the below diff.. but it is probably better to check if one could combine these llme's at a higher level? Or at least free the old LLME and use the new one? My last problem right now is that LLC data is not sent to the GGSN.. :}
<0012> gprs_llc.c:543 LLC SAPI=1 C FCS=0xd85a2cCMD=UI DATA <0012> gprs_llc.c:194 TLLI 0xa44240f7 is foreign, converting to local TLLI 0xe44240f7 <0012> gprs_llc.c:409 LLC TX: unknown TLLI 0xa44240f7, creating LLME on the fly Assert failed msgb_tlli(msg) == mmctx->llme->tlli || msgb_tlli(msg) == mmctx->llme->old_tlli gprs_llc.c:106 backtrace() returned 20 addresses /home/ich/install/openbsc/lib/libosmocore.so.4(osmo_generate_backtrace+0x16) [0xb7bb6a36] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x8049dc5] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x8052085] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x804a326] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x804a8ff] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x804ac42] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x804b3e5] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x804bd9f] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x804d23c] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x8052a24] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn(bssgp_prim_cb+0x55) [0x804f5d4] /home/ich/install/openbsc/lib/libosmogb.so.2(bssgp_rcvmsg+0x3b8) [0xb7b626b8] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x804f521] /home/ich/install/openbsc/lib/libosmogb.so.2(gprs_ns_rcvmsg+0x8c7) [0xb7b5ecf7] /home/ich/install/openbsc/lib/libosmogb.so.2(+0x4311) [0xb7b5f311] /home/ich/install/openbsc/lib/libosmocore.so.4(osmo_select_main+0x192) [0xb7bb2ed2] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x804fbfd] /lib/i386-linux-gnu/i686/cmov/libc.so.6(__libc_start_main+0xf5) [0xb796a8f5] /home/ich/source/gsm/openbsc/openbsc/src/gprs/osmo-sgsn() [0x8049ded]
Program received signal SIGABRT, Aborted. 0xb7fde424 in __kernel_vsyscall () (gdb) bt #0 0xb7fde424 in __kernel_vsyscall () #1 0xb797f83f in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56 #2 0xb7982cf3 in __GI_abort () at abort.c:90 #3 0x08049dca in _bssgp_tx_dl_ud (mmctx=<optimized out>, msg=<optimized out>) at gprs_llc.c:105 #4 0x08052085 in gprs_llc_tx_ui (msg=0x8092ba8, sapi=1 '\001', command=1, mmctx=0x8092700) at gprs_llc.c:417 #5 0x0804a326 in gsm48_gmm_sendmsg (msg=0x8092ba8, command=1, mm=0x8092700) at gprs_gmm.c:241 #6 0x0804a8ff in gsm48_tx_gmm_id_req (mm=0x8092700, id_type=2 '\002') at gprs_gmm.c:454 #7 0x0804ac42 in gsm48_gmm_authorize (ctx=0x8092700, t3350_mode=GMM_T3350_MODE_ATT) at gprs_gmm.c:560 #8 0x0804b3e5 in gsm48_rx_gmm_att_req (ctx=0x8092700, msg=0x8090f98, llme=0x8091ce8) at gprs_gmm.c:765 #9 0x0804bd9f in gsm0408_rcv_gmm (mmctx=0x0, msg=0x8090f98, llme=0x8091ce8) at gprs_gmm.c:1039 #10 0x0804d23c in gsm0408_gprs_rcvmsg (msg=msg@entry=0x8090f98, llme=0x8091ce8) at gprs_gmm.c:1566 #11 0x08052a24 in gprs_llc_rcvmsg (msg=0x8090f98, tv=0xbfffdcb0) at gprs_llc.c:874 #12 0x0804f5d4 in bssgp_prim_cb (oph=oph@entry=0xbfffdc8c, ctx=ctx@entry=0x0) at sgsn_main.c:114 #13 0xb7b626b8 in bssgp_rx_ul_ud (tp=0xbfffdcb0, msg=0x8090f98, ctx=<optimized out>) at gprs_bssgp.c:398 #14 bssgp_rx_ptp (bctx=0x8091a08, tp=0xbfffdcb0, msg=0x8090f98) at gprs_bssgp.c:820 #15 bssgp_rcvmsg (msg=0x8090f98) at gprs_bssgp.c:1016 #16 0x0804f521 in sgsn_ns_cb (event=GPRS_NS_EVT_UNIT_DATA, nsvc=0x8090740, msg=0x8090f98, bvci=1801) at sgsn_main.c:92 #17 0xb7b5ecf7 in gprs_ns_rx_unitdata (msg=0x8090f98, nsvc=0x8090740) at gprs_ns.c:616 #18 gprs_ns_rcvmsg (nsi=nsi@entry=0x807fd38, msg=msg@entry=0x8090f98, saddr=saddr@entry=0xbfffedc0, ll=ll@entry=GPRS_NS_LL_UDP) at gprs_ns.c:841 #19 0xb7b5f311 in handle_nsip_read (bfd=0x807fd58) at gprs_ns.c:991
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c index b637acb..f0930f6 100644 --- a/openbsc/src/gprs/gprs_llc.c +++ b/openbsc/src/gprs/gprs_llc.c @@ -84,6 +84,22 @@ struct gprs_llc_hdr_parsed {
static struct gprs_llc_llme *llme_alloc(uint32_t tlli);
+/* If the TLLI is foreign, return its local version */ +static inline uint32_t tlli_foreign2local(uint32_t tlli) +{ + uint32_t new_tlli; + + if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { + new_tlli = tlli | 0x40000000; + DEBUGP(DLLC, "TLLI 0x%08x is foreign, converting to " + "local TLLI 0x%08x\n", tlli, new_tlli); + } else + new_tlli = tlli; + + return new_tlli; +} + + /* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) @@ -103,7 +119,8 @@ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
/* make sure we only send it to the right llme */ OSMO_ASSERT(msgb_tlli(msg) == mmctx->llme->tlli - || msgb_tlli(msg) == mmctx->llme->old_tlli); + || msgb_tlli(msg) == mmctx->llme->old_tlli + || tlli_foreign2local(msgb_tlli(msg)) == mmctx->llme->tlli); } memcpy(&dup.qos_profile, qos_profile_default, sizeof(qos_profile_default)); @@ -183,21 +200,6 @@ static const struct gprs_llc_params llc_default_params[] = { LLIST_HEAD(gprs_llc_llmes); void *llc_tall_ctx;
-/* If the TLLI is foreign, return its local version */ -static inline uint32_t tlli_foreign2local(uint32_t tlli) -{ - uint32_t new_tlli; - - if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { - new_tlli = tlli | 0x40000000; - DEBUGP(DLLC, "TLLI 0x%08x is foreign, converting to " - "local TLLI 0x%08x\n", tlli, new_tlli); - } else - new_tlli = tlli; - - return new_tlli; -} - /* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */ static struct gprs_llc_lle *lle_by_tlli_sapi(const uint32_t tlli, uint8_t sapi) { @@ -224,8 +226,12 @@ static struct gprs_llc_lle *lle_for_rx_by_tlli_sapi(const uint32_t tlli, /* Maybe it is a routing area update but we already know this sapi? */ if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { lle = lle_by_tlli_sapi(tlli_foreign2local(tlli), sapi); - if (lle) + if (lle) { + LOGP(DLLC, LOGL_NOTICE, + "LLC RX: Found a local entry for TLLI 0x%08x\n", + tlli); return lle; + } }
/* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded, @@ -403,6 +409,8 @@ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
/* look-up or create the LL Entity for this (TLLI, SAPI) tuple */ lle = lle_by_tlli_sapi(msgb_tlli(msg), sapi); + if (!lle) + lle = lle_by_tlli_sapi(tlli_foreign2local(msgb_tlli(msg)), sapi); if (!lle) { struct gprs_llc_llme *llme; LOGP(DLLC, LOGL_ERROR, "LLC TX: unknown TLLI 0x%08x, "
On Tue, Jul 30, 2013 at 10:05:24AM +0200, Holger Hans Peter Freyther wrote:
I added the below diff.. but it is probably better to check if one could combine these llme's at a higher level? Or at least free the old LLME and use the new one? My last problem right now is that LLC data is not sent to the GGSN.. :}
*aehm* i had a hacked-up libgtp... for doing some perf measurements of tun0... I am currently attempting to watch youtube on my E71.. but it is buffering a lot and the 10s PDU lifetime does not appear big eough.. so there are a lot of re-transmissions going on.
/* make sure we only send it to the right llme */ OSMO_ASSERT(msgb_tlli(msg) == mmctx->llme->tlli
|| msgb_tlli(msg) == mmctx->llme->old_tlli);
|| msgb_tlli(msg) == mmctx->llme->old_tlli|| tlli_foreign2local(msgb_tlli(msg)) == mmctx->llme->tlli);
and add the same for the old_tlli. :}
Hi Holger,
On Tue, Jul 30, 2013 at 09:20:38AM +0200, Holger Hans Peter Freyther wrote:
The engineer in me.. used git gui blame on the file and I found the commit that added the foreign tlli lookup.
thanks.
1.) I do the lookup based on the tlli... 2.) if the tlli is foreign.. I convert it and make another lookup 3.) I create the LLME on the fly or return NULL
seems reasonable.
Or shall we do this... "oh we know this subscriber already" at a higher level in GMM? E.g. notice that it is a routing area update... and then free the new llme and do a tlli assignment for the foreign tlli?
That is probably the cleaner approach. I'm wondering why there is no information in the spec regarding that case, or at least why I haven't been stumbling accross it.
If it is done at LLC level, it seems the more 'defensive' / tolerant approach, because we don't expect the first LLC message on a new BTS to be a GMM/RAU message. I'm not sure if this is guaranteed by the specs.
If the first LLC message on the new cell is a user data message, then the GMM/RAU based approach of adressing this problem would not work.
Regards, Harald
Hi Holger,
On Mon, Jul 29, 2013 at 08:26:32AM +0200, Holger Hans Peter Freyther wrote:
Okay. That does make sense. Thanks for going through the specs.
No problem, I looked at this TLLI issue again and again over the years, and still there seem to be some bugs in the code :/
I'm not really sure if a PCU really needs this, at least not as long as we always include the IMSI as part of BSSGP Downlink Unitdata. At that point, the PCU can look-up its MS-contaxt based on the IMSI. The spec says:
The osmo-pcu is broken in this regard. It will only use the TLLI to identify the temporary block flow (tbf). I will shuffle the code around to resolve that limitation and create unit tests for that.
This is indeed broken. The TBF should be looked up by the IMSI. Looking it up by TLLI should only be neccessary if the BSSGP-DL-UD has no IMSI in it (e.g. the IDENTITY REEQUEST (IMSI) after an ATTACH REQUEST with unknown P-TMSI).
So this would be the case if we get an ATTACH_REQ or RAU_REQ with P-TMSI only, but have not yet sent the IDENTITY REQUEST or not received the IDENTITY RESPONSE yet. At that point, we don't issue any llgmm_assign() requests to LLC yet, and thus are guaranteed that we always know the IMSI at the time we have a TLLI change.
Do you agree?
Yes, but we do issue llgm_assign in that case (and that is the case I can simulate right now). I think the llgm_assign should just be for the foriegn tlli we received (and not from the one dervied by the P-TMSI that we didn't allocate).
But the LLGMM-ASSIGN.req that we issue at that point (with old and new tlli != 0xffffffff) explicitly enables both old and new IMSI for uplink, see my other mail. This is what I believe to be the right thing at that point. In this case (old and new != all-1) both the new and the old tlli are accepted.
If TLLI Old ≠ all 1's and TLLI New ≠ all 1's then TLLI Old and TLLI New are assigned, and TLLI New shall be used when (re-)transmitting LLC frames. Both TLLI Old and TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
but for the above ATTACH case with a unknown P-TMSI/TLLI. We will do the assignment but use TLLI Old for the transmissionof any frame.
Yes, we basically override the LLC logic of "accept old and new in Rx, use new in tx" specifically by setting msgb_tlli(msg) manually to the old tlli.
The point is, until the GMM message assigning a new P-TMSI has been successfully received by the MS, the MS has no clue what the new TLLI might be. So until the SGSN knows that this GMM message has been successfully received by the MS, the SGSN should continue to use the old TLLI in downlink. Only once the MS has acknowledged in GMM that the new P-TMSI has been received (by RAU_COMPLETE / ATTACH_COMPLETE), the SGSN knows that the MS has successfully switched to the new TLLI. Also, at the same time, any frame that wer receive from the MS using the new TLLI will implicitly tell us (even at the LLC layer, without involving GMM) that the MS has received the new P-TMSI, because it starts to use it.
The specs seem to have some contradiction here. If you look at 24.008 it says in 4.7.1.4.1:
Although the MS derives a local TLLI for addressing at lower layers, the network should not assume that it will receive only LLC frames using a local TLLI. Immediately after the successful GPRS attach or routing area update procedure, the network must be prepared to continue accepting LLC frames from the MS still using the foreign TLLI.
Which is a bit odd. Why would we receive old TLLI _after_ the respective COMPLETE messages?
But at least for downlink, it is pretty clear:
In both cases, the MS shall acknowledge the reception of the assigned P-TMSI to the network. After receipt of the acknowledgement, the network shall use the local TLLI for addressing at lower layers.
Furthermore:
Upon receipt of a GMM message containing a new P-TMSI the MS shall consider the new P-TMSI and new RAI and also the old P-TMSI and old RAI as valid in order to react to paging requests and downlink transmission of LLC frames.
For uplink transmission of LLC frames the new P-TMSI shall be used.
i.e. the ATTACH_COMPLETE/RAU_COMPLETE should be the first message with the new TLLI.
And:
The MS shall consider the old P-TMSI and old RAI as invalid as soon as an LLC frame is received with the local TLLI derived from the new P-TMSI.
So maybe we should send an empty frame (LLGMM-TRIGGER.req? but that exists only on the MS side of LLC, not the SGSN side) in downlink after the RAU_COMPLETE / ATTACH_COMPLETE with the new TLLI? However, the transmission is unreliable and it could get lost. So I guess there's no point in even trying to send a message with new TLLI to the MS, if we don't get a layer3-acknowledgement back.
So the only really odd part is that 04.64 states that after LLGMM-ASSIGN(old!=ff, new!=ff) we should use the new TLLI for downlink transmit, while 24.008 states that we should continue to use the old TLLI for downlink transmit until we receive the ATTACH_COMPLETE and/or first uplink LLC message with new TLLI.
As I don't know of what use the 04.64-specified behavior (receive old+new, transmit with new only) should be, I think it might be wrong and actually the behavior should be that tx should be done with old TLLI.
In order to still have LLGMM-ASSIGN.req like specified in 04.64 _and_ fulfill the 24.008 requirements, we override the outgoing TLLI by using msgb_tlli(msg) = mmctx->tlli (which is the old tlli) in GMM.
Later, when we get the ATTACH_COMPLETE / RAU_COMPLETE from the MS, we
a) set mmctx->tlli = mmctx->tlli_new b) issue a LLGMM-ASSIGN.req primitive to LLC with old TMSI 0xffffffff and new TMSI = mmctx->tlli_new. According to spec, this is the following case:
If TLLI Old = all 1's and TLLI New ≠ all 1's then TLLI New shall be assigned and used when (re-)transmitting LLC frames. If a TLLI Old ≠ all 1's was assigned to the LLME, then TLLI Old is unassigned. Only TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
but that means that llme->old_tlli will be all 1's and that even if we want to.. in gprs_llc we could not set dup.tlli to the previous tlli? So the next download unitdata can not have the IE_TLLI set?
yes, this appears to be correct (and thus maybe good that dup.tlli is not set in current code). In order to do this as per spec (which is stupid, as the PCU has the IMSI to identify the MS), we would probably have to keep a flag in the LLME indicating that 'recently the old tlli was unassigned'. During transmission of the next downlink frame (which may be hours later!) we then include that 'very_old_tlli' as second IE_TLLI in the BSSGP-DL-UD. However, if it is really that much later, then there is no TBF anymore in the PCU anyway ;) So this all only makes sense if the SGSN is _soon_ after the TLLI/P-TMSI change sending moere data (signalling or user-data) in downlink.
As TBFs have a very short life-time, I would currently argue to simply ignore (+ document) this behavior for Gb. Make sure that osmo-pcu uses the ISMI if present, and hope that other PCU implementors also do it that way.
Okay, I will modify the lle_by_tlli_sapi look-up to not use the foreign conversion at all. This would fix my case..
Great. I will do some manual testing with sysmobts/osmo-pcu/osmo-sgsn using that patch later tonight.
but given the lack of unit tests I don't know what I break. :)
ah well, just be brave :)
Regards, Harald
Hi Holger,
just to explicitly respond to this sentence in your first message on the subject:
On Sun, Jul 28, 2013 at 10:01:04PM +0200, Holger Hans Peter Freyther wrote:
So this means that for the entire GPRS attach procedure we will use the initial foreign TLLI....
Which I believe is correct. The RAU_COMPLETE / ATTACH_COMPLETE should be the first message in downlink containing the new TLLI from my point of view.
On Mon, Jul 29, 2013 at 10:35:47AM +0800, Harald Welte wrote:
Hi Holger,
just to explicitly respond to this sentence in your first message on the subject:
On Sun, Jul 28, 2013 at 10:01:04PM +0200, Holger Hans Peter Freyther wrote:
So this means that for the entire GPRS attach procedure we will use the initial foreign TLLI....
Which I believe is correct. The RAU_COMPLETE / ATTACH_COMPLETE should be the first message in downlink containing the new TLLI from my point of view.
Thank you for the detailed answer (in the other email). But doesn't this mean that the gprs_llgmm_assign in gsm48_rx_gmm_att_req is a bit early?
E.g. something like this:
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c index f7a5cde..b981fb7 100644 --- a/openbsc/src/gprs/gprs_gmm.c +++ b/openbsc/src/gprs/gprs_gmm.c @@ -758,7 +758,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, ctx->tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL);
/* Inform LLC layer about new TLLI but keep old active */ - gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new, + gprs_llgmm_assign(ctx->llme, 0xffffffff,, ctx->tlli, GPRS_ALGO_GEA0, NULL);
DEBUGPC(DMM, "\n");
and then in GMM_ATTACH_COMPLETE we will assign tlli_new?
Hi Holger,
On Mon, Jul 29, 2013 at 08:08:18AM +0200, Holger Hans Peter Freyther wrote:
Which I believe is correct. The RAU_COMPLETE / ATTACH_COMPLETE should be the first message in downlink containing the new TLLI from my point of view.
Thank you for the detailed answer (in the other email). But doesn't this mean that the gprs_llgmm_assign in gsm48_rx_gmm_att_req is a bit early?
E.g. something like this:
/* Inform LLC layer about new TLLI but keep old active */
gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new,
gprs_llgmm_assign(ctx->llme, 0xffffffff,, ctx->tlli,
Why?
This would be the following case:
LLGMM-ASSIGN.req (7.2.1.1 of 04.64): The TLLI Old and TLLI New parameters shall be interpreted as follows: If TLLI Old = all 1's and TLLI New ≠ all 1's then TLLI New shall be assigned and used when (re-)transmitting LLC frames. If a TLLI Old ≠ all 1's was assigned to the LLME, then TLLI Old is unassigned. Only TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
This would mean that the tlli_old (which is stored in the LLME up to the call to your proposed "gprs_llgmm_assign(ctx->llme, 0xffffffff, ctx->tlli") would no longer be accepted on the Rx side.
However, what we want at this point is to accept old _and_ new TLLI, which is:
LLGMM-ASSIGN.req (7.2.1.1 of 04.64): The TLLI Old and TLLI New parameters shall be interpreted as follows: If TLLI Old ≠ all 1's and TLLI New ≠ all 1's then TLLI Old and TLLI New are assigned, and TLLI New shall be used when (re-)transmitting LLC frames. Both TLLI Old and TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
So we accept both old and new from the MS, which is what we want. However, to my understanding, we actually don't want to use the new TLLI for TX, which is why we use msgb_tlli(msg) = old_tlli to explicitly request that tlli to be used.
So I still think that at least _this_ part of the existing code is doing it right.
Regards, Harald
On Mon, Jul 29, 2013 at 06:04:05PM +0800, Harald Welte wrote:
Hi Holger,
Good Evening,
/* Inform LLC layer about new TLLI but keep old active */
gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new,
gprs_llgmm_assign(ctx->llme, 0xffffffff,, ctx->tlli,Why?
Well, in case of GPRS Attach of a roaming subscriber: ctx->tlli_new is the local TLLI based on the P-TMSI we didn't assign ctx->tlli is the foreign TLLI based on this P-TMSI.
In that case tlli_new will never be used in the traffic, e.g. we would assig a new P-TMSI based on the GPRS Attach. Given how much/little I understand right now.. the tlli_new will not be used by us or the phone during the attachment procedure.
LLGMM-ASSIGN.req (7.2.1.1 of 04.64): The TLLI Old and TLLI New parameters shall be interpreted as follows: If TLLI Old = all 1's and TLLI New ≠ all 1's then TLLI New shall be assigned and used when (re-)transmitting LLC frames. If a TLLI Old ≠ all 1's was assigned to the LLME, then TLLI Old is unassigned. Only TLLI New shall be accepted when received from the peer. It shall be treated as a TLLI change according to subclause 8.3.2.
This would mean that the tlli_old (which is stored in the LLME up to the call to your proposed "gprs_llgmm_assign(ctx->llme, 0xffffffff, ctx->tlli") would no longer be accepted on the Rx side.
So I assume that:
llme->old_tlli = 0xff...; llme->tlli = a foreign TLLI;
gprs_llgmm_assign(llme, 0xff.., llme->tlli);
this would be the "TLLI Assignment 8.3.1" case? It would also reset all the lle's.
So I still think that at least _this_ part of the existing code is doing it right.
Okay, it is certainly not a bug and might even be correct. So there is no point in changing this code.