1 /* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
3 /* (C) 2009-2012 by Harald Welte <laforge@gnumonks.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include <netinet/in.h>
27 #include <osmocom/core/msgb.h>
28 #include <osmocom/core/rate_ctr.h>
29 #include <osmocom/gsm/tlv.h>
30 #include <osmocom/core/talloc.h>
31 #include <osmocom/gprs/gprs_bssgp.h>
32 #include <osmocom/gprs/gprs_bssgp_bss.h>
33 #include <osmocom/gprs/gprs_ns.h>
35 #include "common_vty.h"
37 uint8_t *bssgp_msgb_tlli_put(struct msgb
*msg
, uint32_t tlli
)
39 uint32_t _tlli
= htonl(tlli
);
40 return msgb_tvlv_put(msg
, BSSGP_IE_TLLI
, 4, (uint8_t *) &_tlli
);
43 /*! \brief GMM-SUSPEND.req (Chapter 10.3.6) */
44 int bssgp_tx_suspend(uint16_t nsei
, uint32_t tlli
,
45 const struct gprs_ra_id
*ra_id
)
47 struct msgb
*msg
= bssgp_msgb_alloc();
48 struct bssgp_normal_hdr
*bgph
=
49 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
52 LOGP(DBSSGP
, LOGL_NOTICE
, "BSSGP (BVCI=0) Tx SUSPEND (TLLI=0x%04x)\n",
54 msgb_nsei(msg
) = nsei
;
55 msgb_bvci(msg
) = 0; /* Signalling */
56 bgph
->pdu_type
= BSSGP_PDUT_SUSPEND
;
58 bssgp_msgb_tlli_put(msg
, tlli
);
60 gsm48_construct_ra(ra
, ra_id
);
61 msgb_tvlv_put(msg
, BSSGP_IE_ROUTEING_AREA
, 6, ra
);
63 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
66 /*! \brief GMM-RESUME.req (Chapter 10.3.9) */
67 int bssgp_tx_resume(uint16_t nsei
, uint32_t tlli
,
68 const struct gprs_ra_id
*ra_id
, uint8_t suspend_ref
)
70 struct msgb
*msg
= bssgp_msgb_alloc();
71 struct bssgp_normal_hdr
*bgph
=
72 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
75 LOGP(DBSSGP
, LOGL_NOTICE
, "BSSGP (BVCI=0) Tx RESUME (TLLI=0x%04x)\n",
77 msgb_nsei(msg
) = nsei
;
78 msgb_bvci(msg
) = 0; /* Signalling */
79 bgph
->pdu_type
= BSSGP_PDUT_RESUME
;
81 bssgp_msgb_tlli_put(msg
, tlli
);
83 gsm48_construct_ra(ra
, ra_id
);
84 msgb_tvlv_put(msg
, BSSGP_IE_ROUTEING_AREA
, 6, ra
);
86 msgb_tvlv_put(msg
, BSSGP_IE_SUSPEND_REF_NR
, 1, &suspend_ref
);
88 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
91 /*! \brief Transmit RA-CAPABILITY-UPDATE (10.3.3) */
92 int bssgp_tx_ra_capa_upd(struct bssgp_bvc_ctx
*bctx
, uint32_t tlli
, uint8_t tag
)
94 struct msgb
*msg
= bssgp_msgb_alloc();
95 struct bssgp_normal_hdr
*bgph
=
96 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
98 LOGP(DBSSGP
, LOGL_NOTICE
, "BSSGP (BVCI=%u) Tx RA-CAPA-UPD (TLLI=0x%04x)\n",
101 /* set NSEI and BVCI in msgb cb */
102 msgb_nsei(msg
) = bctx
->nsei
;
103 msgb_bvci(msg
) = bctx
->bvci
;
105 bgph
->pdu_type
= BSSGP_PDUT_RA_CAPA_UDPATE
;
106 bssgp_msgb_tlli_put(msg
, tlli
);
108 msgb_tvlv_put(msg
, BSSGP_IE_TAG
, 1, &tag
);
110 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
113 /* first common part of RADIO-STATUS */
114 static struct msgb
*common_tx_radio_status(struct bssgp_bvc_ctx
*bctx
)
116 struct msgb
*msg
= bssgp_msgb_alloc();
117 struct bssgp_normal_hdr
*bgph
=
118 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
120 LOGP(DBSSGP
, LOGL_NOTICE
, "BSSGP (BVCI=%u) Tx RADIO-STATUS ",
123 /* set NSEI and BVCI in msgb cb */
124 msgb_nsei(msg
) = bctx
->nsei
;
125 msgb_bvci(msg
) = bctx
->bvci
;
127 bgph
->pdu_type
= BSSGP_PDUT_RADIO_STATUS
;
132 /* second common part of RADIO-STATUS */
133 static int common_tx_radio_status2(struct msgb
*msg
, uint8_t cause
)
135 msgb_tvlv_put(msg
, BSSGP_IE_CAUSE
, 1, &cause
);
136 LOGPC(DBSSGP
, LOGL_NOTICE
, "CAUSE=%u\n", cause
);
138 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
141 /*! \brief Transmit RADIO-STATUS for TLLI (10.3.5) */
142 int bssgp_tx_radio_status_tlli(struct bssgp_bvc_ctx
*bctx
, uint8_t cause
,
145 struct msgb
*msg
= common_tx_radio_status(bctx
);
149 bssgp_msgb_tlli_put(msg
, tlli
);
150 LOGPC(DBSSGP
, LOGL_NOTICE
, "TLLI=0x%08x ", tlli
);
152 return common_tx_radio_status2(msg
, cause
);
155 /*! \brief Transmit RADIO-STATUS for TMSI (10.3.5) */
156 int bssgp_tx_radio_status_tmsi(struct bssgp_bvc_ctx
*bctx
, uint8_t cause
,
159 struct msgb
*msg
= common_tx_radio_status(bctx
);
160 uint32_t _tmsi
= htonl(tmsi
);
164 msgb_tvlv_put(msg
, BSSGP_IE_TMSI
, 4, (uint8_t *)&_tmsi
);
165 LOGPC(DBSSGP
, LOGL_NOTICE
, "TMSI=0x%08x ", tmsi
);
167 return common_tx_radio_status2(msg
, cause
);
170 /*! \brief Transmit RADIO-STATUS for IMSI (10.3.5) */
171 int bssgp_tx_radio_status_imsi(struct bssgp_bvc_ctx
*bctx
, uint8_t cause
,
174 struct msgb
*msg
= common_tx_radio_status(bctx
);
176 int imsi_len
= gsm48_generate_mid_from_imsi(mi
, imsi
);
181 /* strip the MI type and length values (2 bytes) */
183 msgb_tvlv_put(msg
, BSSGP_IE_IMSI
, imsi_len
-2, mi
+2);
184 LOGPC(DBSSGP
, LOGL_NOTICE
, "IMSI=%s ", imsi
);
186 return common_tx_radio_status2(msg
, cause
);
189 /*! \brief Transmit FLUSH-LL-ACK (Chapter 10.4.2) */
190 int bssgp_tx_flush_ll_ack(struct bssgp_bvc_ctx
*bctx
, uint32_t tlli
,
191 uint8_t action
, uint16_t bvci_new
,
194 struct msgb
*msg
= bssgp_msgb_alloc();
195 struct bssgp_normal_hdr
*bgph
=
196 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
197 uint16_t _bvci_new
= htons(bvci_new
);
198 uint32_t _oct_aff
= htonl(num_octets
& 0xFFFFFF);
200 msgb_nsei(msg
) = bctx
->nsei
;
201 msgb_bvci(msg
) = 0; /* Signalling */
202 bgph
->pdu_type
= BSSGP_PDUT_FLUSH_LL_ACK
;
204 bssgp_msgb_tlli_put(msg
, tlli
);
205 msgb_tvlv_put(msg
, BSSGP_IE_FLUSH_ACTION
, 1, &action
);
206 if (action
== 1) /* transferred */
207 msgb_tvlv_put(msg
, BSSGP_IE_BVCI
, 2, (uint8_t *) &_bvci_new
);
208 msgb_tvlv_put(msg
, BSSGP_IE_NUM_OCT_AFF
, 3, (uint8_t *) &_oct_aff
);
210 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
213 /*! \brief Transmit LLC-DISCARDED (Chapter 10.4.3) */
214 int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx
*bctx
, uint32_t tlli
,
215 uint8_t num_frames
, uint32_t num_octets
)
217 struct msgb
*msg
= bssgp_msgb_alloc();
218 struct bssgp_normal_hdr
*bgph
=
219 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
220 uint16_t _bvci
= htons(bctx
->bvci
);
221 uint32_t _oct_aff
= htonl(num_octets
& 0xFFFFFF);
223 LOGP(DBSSGP
, LOGL_NOTICE
, "BSSGP (BVCI=%u) Tx LLC-DISCARDED "
224 "TLLI=0x%04x, FRAMES=%u, OCTETS=%u\n", bctx
->bvci
, tlli
,
225 num_frames
, num_octets
);
226 msgb_nsei(msg
) = bctx
->nsei
;
227 msgb_bvci(msg
) = 0; /* Signalling */
228 bgph
->pdu_type
= BSSGP_PDUT_LLC_DISCARD
;
230 bssgp_msgb_tlli_put(msg
, tlli
);
232 msgb_tvlv_put(msg
, BSSGP_IE_LLC_FRAMES_DISCARDED
, 1, &num_frames
);
233 msgb_tvlv_put(msg
, BSSGP_IE_BVCI
, 2, (uint8_t *) &_bvci
);
234 msgb_tvlv_put(msg
, BSSGP_IE_NUM_OCT_AFF
, 3, ((uint8_t *) &_oct_aff
) + 1);
236 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
239 /*! \brief Transmit a BVC-BLOCK message (Chapter 10.4.8) */
240 int bssgp_tx_bvc_block(struct bssgp_bvc_ctx
*bctx
, uint8_t cause
)
242 struct msgb
*msg
= bssgp_msgb_alloc();
243 struct bssgp_normal_hdr
*bgph
=
244 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
245 uint16_t _bvci
= htons(bctx
->bvci
);
247 LOGP(DBSSGP
, LOGL_NOTICE
, "BSSGP (BVCI=%u) Tx BVC-BLOCK "
248 "CAUSE=%u\n", bctx
->bvci
, cause
);
250 msgb_nsei(msg
) = bctx
->nsei
;
251 msgb_bvci(msg
) = 0; /* Signalling */
252 bgph
->pdu_type
= BSSGP_PDUT_BVC_BLOCK
;
254 msgb_tvlv_put(msg
, BSSGP_IE_BVCI
, 2, (uint8_t *) &_bvci
);
255 msgb_tvlv_put(msg
, BSSGP_IE_CAUSE
, 1, &cause
);
257 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
260 /*! \brief Transmit a BVC-UNBLOCK message (Chapter 10.4.10) */
261 int bssgp_tx_bvc_unblock(struct bssgp_bvc_ctx
*bctx
)
263 struct msgb
*msg
= bssgp_msgb_alloc();
264 struct bssgp_normal_hdr
*bgph
=
265 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
266 uint16_t _bvci
= htons(bctx
->bvci
);
268 LOGP(DBSSGP
, LOGL_NOTICE
, "BSSGP (BVCI=%u) Tx BVC-BLOCK\n", bctx
->bvci
);
270 msgb_nsei(msg
) = bctx
->nsei
;
271 msgb_bvci(msg
) = 0; /* Signalling */
272 bgph
->pdu_type
= BSSGP_PDUT_BVC_UNBLOCK
;
274 msgb_tvlv_put(msg
, BSSGP_IE_BVCI
, 2, (uint8_t *) &_bvci
);
276 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
279 /*! \brief Transmit a BVC-RESET message (Chapter 10.4.12) */
280 int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx
*bctx
, uint16_t bvci
, uint8_t cause
)
282 struct msgb
*msg
= bssgp_msgb_alloc();
283 struct bssgp_normal_hdr
*bgph
=
284 (struct bssgp_normal_hdr
*) msgb_put(msg
, sizeof(*bgph
));
285 uint16_t _bvci
= htons(bvci
);
287 LOGP(DBSSGP
, LOGL_NOTICE
, "BSSGP (BVCI=%u) Tx BVC-RESET "
288 "CAUSE=%u\n", bvci
, cause
);
290 msgb_nsei(msg
) = bctx
->nsei
;
291 msgb_bvci(msg
) = 0; /* Signalling */
292 bgph
->pdu_type
= BSSGP_PDUT_BVC_RESET
;
294 msgb_tvlv_put(msg
, BSSGP_IE_BVCI
, 2, (uint8_t *) &_bvci
);
295 msgb_tvlv_put(msg
, BSSGP_IE_CAUSE
, 1, &cause
);
296 if (bvci
!= BVCI_PTM
) {
297 uint8_t bssgp_cid
[8];
298 bssgp_create_cell_id(bssgp_cid
, &bctx
->ra_id
, bctx
->cell_id
);
299 msgb_tvlv_put(msg
, BSSGP_IE_CELL_ID
, sizeof(bssgp_cid
), bssgp_cid
);
301 /* Optional: Feature Bitmap */
303 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
307 /*! \brief RL-UL-UNITDATA.req (Chapter 10.2.2) */
308 int bssgp_tx_ul_ud(struct bssgp_bvc_ctx
*bctx
, uint32_t tlli
,
309 const uint8_t *qos_profile
, struct msgb
*llc_pdu
)
311 struct msgb
*msg
= llc_pdu
;
312 uint8_t bssgp_cid
[8];
313 struct bssgp_ud_hdr
*budh
;
315 /* FIXME: First push alignment octets, if rqd */
317 /* FIXME: Optional LSA Identifier List, PFI */
319 /* Cell Identifier */
320 bssgp_create_cell_id(bssgp_cid
, &bctx
->ra_id
, bctx
->cell_id
);
321 msgb_tvlv_push(msg
, BSSGP_IE_CELL_ID
, sizeof(bssgp_cid
), bssgp_cid
);
323 /* User Data Header */
324 budh
= (struct bssgp_ud_hdr
*) msgb_push(msg
, sizeof(*budh
));
325 budh
->tlli
= htonl(tlli
);
326 memcpy(budh
->qos_profile
, qos_profile
, 3);
327 budh
->pdu_type
= BSSGP_PDUT_UL_UNITDATA
;
329 /* set NSEI and BVCI in msgb cb */
330 msgb_nsei(msg
) = bctx
->nsei
;
331 msgb_bvci(msg
) = bctx
->bvci
;
333 rate_ctr_inc(&bctx
->ctrg
->ctr
[BSSGP_CTR_PKTS_OUT
]);
334 rate_ctr_add(&bctx
->ctrg
->ctr
[BSSGP_CTR_BYTES_OUT
], msg
->len
);
336 return gprs_ns_sendmsg(bssgp_nsi
, msg
);
339 /* Parse a single GMM-PAGING.req to a given NSEI/NS-BVCI */
340 int bssgp_rx_paging(struct bssgp_paging_info
*pinfo
,
343 struct bssgp_normal_hdr
*bgph
=
344 (struct bssgp_normal_hdr
*) msgb_bssgph(msg
);
345 struct tlv_parsed tp
;
349 memset(ra
, 0, sizeof(ra
));
351 data_len
= msgb_bssgp_len(msg
) - sizeof(*bgph
);
352 rc
= bssgp_tlv_parse(&tp
, bgph
->data
, data_len
);
356 switch (bgph
->pdu_type
) {
357 case BSSGP_PDUT_PAGING_PS
:
358 pinfo
->mode
= BSSGP_PAGING_PS
;
360 case BSSGP_PDUT_PAGING_CS
:
361 pinfo
->mode
= BSSGP_PAGING_CS
;
368 if (!TLVP_PRESENT(&tp
, BSSGP_IE_IMSI
))
371 pinfo
->imsi
= talloc_zero_size(pinfo
, 16);
372 gsm48_mi_to_string(pinfo
->imsi
, sizeof(pinfo
->imsi
),
373 TLVP_VAL(&tp
, BSSGP_IE_IMSI
),
374 TLVP_LEN(&tp
, BSSGP_IE_IMSI
));
377 if (!TLVP_PRESENT(&tp
, BSSGP_IE_DRX_PARAMS
))
379 pinfo
->drx_params
= ntohs(*(uint16_t *)TLVP_VAL(&tp
, BSSGP_IE_DRX_PARAMS
));
382 if (TLVP_PRESENT(&tp
, BSSGP_IE_BSS_AREA_ID
)) {
383 pinfo
->scope
= BSSGP_PAGING_BSS_AREA
;
384 } else if (TLVP_PRESENT(&tp
, BSSGP_IE_LOCATION_AREA
)) {
385 pinfo
->scope
= BSSGP_PAGING_LOCATION_AREA
;
386 memcpy(ra
, TLVP_VAL(&tp
, BSSGP_IE_LOCATION_AREA
),
387 TLVP_LEN(&tp
, BSSGP_IE_LOCATION_AREA
));
388 gsm48_parse_ra(&pinfo
->raid
, ra
);
389 } else if (TLVP_PRESENT(&tp
, BSSGP_IE_ROUTEING_AREA
)) {
390 pinfo
->scope
= BSSGP_PAGING_ROUTEING_AREA
;
391 memcpy(ra
, TLVP_VAL(&tp
, BSSGP_IE_ROUTEING_AREA
),
392 TLVP_LEN(&tp
, BSSGP_IE_ROUTEING_AREA
));
393 gsm48_parse_ra(&pinfo
->raid
, ra
);
394 } else if (TLVP_PRESENT(&tp
, BSSGP_IE_BVCI
)) {
395 pinfo
->scope
= BSSGP_PAGING_BVCI
;
396 pinfo
->bvci
= ntohs(*(uint16_t *)TLVP_VAL(&tp
, BSSGP_IE_BVCI
));
400 /* QoS profile mandatory for PS */
401 if (pinfo
->mode
== BSSGP_PAGING_PS
) {
402 if (!TLVP_PRESENT(&tp
, BSSGP_IE_QOS_PROFILE
))
404 if (TLVP_LEN(&tp
, BSSGP_IE_QOS_PROFILE
) < 3)
407 memcpy(&pinfo
->qos
, TLVP_VAL(&tp
, BSSGP_IE_QOS_PROFILE
),
411 /* Optional (P-)TMSI */
412 if (TLVP_PRESENT(&tp
, BSSGP_IE_TMSI
) &&
413 TLVP_LEN(&tp
, BSSGP_IE_TMSI
) >= 4)
415 pinfo
->ptmsi
= talloc_zero_size(pinfo
, sizeof(uint32_t));
416 *(pinfo
->ptmsi
) = ntohl(*(uint32_t *)
417 TLVP_VAL(&tp
, BSSGP_IE_TMSI
));