blob: 3432bca87ca57212dd6a16e2970148613b1ba441 [file] [log] [blame]
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +04001/* gprs_bssgp_pcu.cpp
2 *
3 * Copyright (C) 2012 Ivan Klyuchnikov
Holger Hans Peter Freyther416ce692013-08-22 08:44:04 +02004 * Copyright (C) 2013 by Holger Hans Peter Freyther
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +04005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <gprs_rlcmac.h>
22#include <gprs_bssgp_pcu.h>
23#include <pcu_l1_if.h>
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +020024#include <tbf.h>
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040025
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +020026static struct gprs_bssgp_pcu the_pcu = { 0, };
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +020027
Andreas Eversberg71cce912013-01-16 13:49:00 +010028extern void *tall_pcu_ctx;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +020029extern uint16_t spoof_mcc, spoof_mnc;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040030
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +020031static void bvc_timeout(void *_priv);
32
Andreas Eversberg00950742012-10-08 12:25:55 +020033static int parse_imsi(struct tlv_parsed *tp, char *imsi)
34{
35 uint8_t imsi_len;
36 uint8_t *bcd_imsi;
37 int i, j;
38
39 if (!TLVP_PRESENT(tp, BSSGP_IE_IMSI))
40 return -EINVAL;
41
42 imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
43 bcd_imsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_IMSI);
44
45 if ((bcd_imsi[0] & 0x08))
46 imsi_len = imsi_len * 2 - 1;
47 else
48 imsi_len = (imsi_len - 1) * 2;
49 for (i = 0, j = 0; j < imsi_len && j < 15; j++)
50 {
51 if (!(j & 1)) {
52 imsi[j] = (bcd_imsi[i] >> 4) + '0';
53 i++;
54 } else
55 imsi[j] = (bcd_imsi[i] & 0xf) + '0';
56 }
57 imsi[j] = '\0';
58
59 return 0;
60}
61
Holger Hans Peter Freyther98fe9452013-07-13 15:23:58 +020062static int parse_ra_cap_ms_class(struct tlv_parsed *tp)
63{
64 bitvec *block;
65 unsigned rp = 0;
66 uint8_t ms_class = 0;
67 uint8_t cap_len;
68 uint8_t *cap;
69
70 if (!TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
71 return ms_class;
72
73 cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
74 cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
75
76 block = bitvec_alloc(cap_len);
77 bitvec_unpack(block, cap);
78 bitvec_read_field(block, rp, 4); // Access Technology Type
79 bitvec_read_field(block, rp, 7); // Length of Access Capabilities
80 bitvec_read_field(block, rp, 3); // RF Power Capability
81 if (bitvec_read_field(block, rp, 1)) // A5 Bits Present
82 bitvec_read_field(block, rp, 7); // A5 Bits
83 bitvec_read_field(block, rp, 1); // ES IND
84 bitvec_read_field(block, rp, 1); // PS
85 bitvec_read_field(block, rp, 1); // VGCS
86 bitvec_read_field(block, rp, 1); // VBS
87 if (bitvec_read_field(block, rp, 1)) { // Multislot Cap Present
88 if (bitvec_read_field(block, rp, 1)) // HSCSD Present
89 bitvec_read_field(block, rp, 5); // Class
90 if (bitvec_read_field(block, rp, 1)) { // GPRS Present
91 ms_class = bitvec_read_field(block, rp, 5); // Class
92 bitvec_read_field(block, rp, 1); // Ext.
93 }
94 if (bitvec_read_field(block, rp, 1)) // SMS Present
95 bitvec_read_field(block, rp, 4); // SMS Value
96 bitvec_read_field(block, rp, 4); // SMS Value
97 }
98
99 bitvec_free(block);
100 return ms_class;
101}
102
Holger Hans Peter Freyther90d5df42013-07-28 21:14:15 +0200103static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400104{
105 struct bssgp_ud_hdr *budh;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400106
Andreas Eversberge6228b32012-07-03 13:36:03 +0200107 uint32_t tlli;
Andreas Eversberge6228b32012-07-03 13:36:03 +0200108 uint8_t *data;
109 uint16_t len;
Andreas Eversberg00950742012-10-08 12:25:55 +0200110 char imsi[16] = "000";
Andreas Eversberg7d7cf542012-06-25 09:26:15 +0200111
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400112 budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
Andreas Eversberge6228b32012-07-03 13:36:03 +0200113 tlli = ntohl(budh->tlli);
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400114
Ivan Kluchnikov5e0df932012-06-12 15:33:52 +0400115 /* LLC_PDU is mandatory IE */
116 if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400117 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200118 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD missing mandatory IE\n", tlli);
Ivan Kluchnikov5e0df932012-06-12 15:33:52 +0400119 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
120 }
Ivan Kluchnikovb172b1b2012-06-07 01:51:49 +0400121
Andreas Eversberge6228b32012-07-03 13:36:03 +0200122 data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
123 len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +0200124 if (len > sizeof(gprs_rlcmac_tbf::llc_frame))
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400125 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200126 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD IE_LLC_PDU too large\n", tlli);
Andreas Eversberge6228b32012-07-03 13:36:03 +0200127 return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400128 }
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400129
Andreas Eversberge13fa2d2012-07-09 17:10:44 +0200130 /* read IMSI. if no IMSI exists, use first paging block (any paging),
131 * because during attachment the IMSI might not be known, so the MS
132 * will listen to all paging blocks. */
Andreas Eversberg00950742012-10-08 12:25:55 +0200133 parse_imsi(tp, imsi);
Andreas Eversberg51ab1342012-07-13 14:52:50 +0200134
135 /* parse ms radio access capability */
Holger Hans Peter Freyther98fe9452013-07-13 15:23:58 +0200136 uint8_t ms_class = parse_ra_cap_ms_class(tp);
Andreas Eversberg51ab1342012-07-13 14:52:50 +0200137
Andreas Eversberg24131bf2012-07-21 11:09:58 +0200138 /* get lifetime */
139 uint16_t delay_csec = 0xffff;
140 if (TLVP_PRESENT(tp, BSSGP_IE_PDU_LIFETIME))
141 {
142 uint8_t lt_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
143 uint16_t *lt = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_PDU_LIFETIME);
144 if (lt_len == 2)
145 delay_csec = ntohs(*lt);
146 else
147 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
148 "PDU_LIFETIME IE\n");
149 } else
150 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP missing mandatory "
151 "PDU_LIFETIME IE\n");
152
Andreas Eversberge13fa2d2012-07-09 17:10:44 +0200153 LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
Ivan Kluchnikov5e0df932012-06-12 15:33:52 +0400154
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +0200155 return tbf_handle(the_pcu.bts, tlli, imsi, ms_class, delay_csec, data, len);
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400156}
Ivan Kluchnikova9e6dc52012-06-17 08:30:06 +0400157
Andreas Eversberg8c3680d2012-10-08 12:30:56 +0200158int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
159{
160 char imsi[16];
161 uint8_t *ptmsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_TMSI);
162 uint16_t ptmsi_len = TLVP_LEN(tp, BSSGP_IE_TMSI);
163
164 LOGP(DBSSGP, LOGL_NOTICE, " P-TMSI = ");
165 for (int i = 0; i < ptmsi_len; i++)
166 {
167 LOGPC(DBSSGP, LOGL_NOTICE, "%02x", ptmsi[i]);
168 }
169 LOGPC(DBSSGP, LOGL_NOTICE, "\n");
170
171 if (parse_imsi(tp, imsi))
172 {
173 LOGP(DBSSGP, LOGL_ERROR, "No IMSI\n");
174 return -EINVAL;
175 }
176
177 return gprs_rlcmac_paging_request(ptmsi, ptmsi_len, imsi);
178}
179
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400180/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
Holger Hans Peter Freyther90d5df42013-07-28 21:14:15 +0200181static int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400182{
183 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
184 uint8_t pdu_type = bgph->pdu_type;
185 unsigned rc = 0;
186
Andreas Eversbergba1cd9b2012-07-25 09:14:09 +0200187 if (!bctx)
188 return -EINVAL;
189
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400190 /* If traffic is received on a BVC that is marked as blocked, the
191 * received PDU shall not be accepted and a STATUS PDU (Cause value:
192 * BVC Blocked) shall be sent to the peer entity on the signalling BVC */
193 if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS)
194 {
195 uint16_t bvci = msgb_bvci(msg);
196 LOGP(DBSSGP, LOGL_NOTICE, "rx BVC_S_BLOCKED\n");
197 return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
198 }
199
200 switch (pdu_type) {
201 case BSSGP_PDUT_DL_UNITDATA:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200202 LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
Holger Hans Peter Freyther416ce692013-08-22 08:44:04 +0200203 if (the_pcu.on_dl_unit_data)
204 the_pcu.on_dl_unit_data(&the_pcu, msg, tp);
Ivan Kluchnikov5e0df932012-06-12 15:33:52 +0400205 gprs_bssgp_pcu_rx_dl_ud(msg, tp);
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400206 break;
207 case BSSGP_PDUT_PAGING_PS:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200208 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400209 break;
210 case BSSGP_PDUT_PAGING_CS:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200211 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400212 break;
213 case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200214 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400215 break;
216 case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200217 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400218 break;
219 case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200220 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400221 break;
222 default:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200223 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400224 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
225 break;
226 }
227 return rc;
228}
229
230/* Receive a BSSGP PDU from a SGSN on a SIGNALLING BVCI */
Holger Hans Peter Freyther90d5df42013-07-28 21:14:15 +0200231static int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400232{
233 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
234 int rc = 0;
235 switch (bgph->pdu_type) {
236 case BSSGP_PDUT_STATUS:
237 /* Some exception has occurred */
238 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
239 /* FIXME: send NM_STATUS.ind to NM */
240 break;
Holger Hans Peter Freythera9744de2013-07-28 19:01:20 +0200241 case BSSGP_PDUT_SUSPEND_ACK:
242 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
243 break;
244 case BSSGP_PDUT_SUSPEND_NACK:
245 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
246 break;
247 case BSSGP_PDUT_BVC_RESET_ACK:
248 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
249 if (!the_pcu.bvc_sig_reset)
250 the_pcu.bvc_sig_reset = 1;
251 else
252 the_pcu.bvc_reset = 1;
253 bvc_timeout(NULL);
254 break;
255 case BSSGP_PDUT_PAGING_PS:
256 LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_PAGING_PS\n");
257 gprs_bssgp_pcu_rx_paging_ps(msg, tp);
258 break;
259 case BSSGP_PDUT_PAGING_CS:
260 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
261 break;
262 case BSSGP_PDUT_RESUME_ACK:
263 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
264 break;
265 case BSSGP_PDUT_RESUME_NACK:
266 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
267 break;
268 case BSSGP_PDUT_FLUSH_LL:
269 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
270 break;
271 case BSSGP_PDUT_BVC_BLOCK_ACK:
272 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
273 break;
274 case BSSGP_PDUT_BVC_UNBLOCK_ACK:
275 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
276 the_pcu.bvc_unblocked = 1;
Holger Hans Peter Freytherc0f16442013-08-22 08:40:33 +0200277 if (the_pcu.on_unblock_ack)
278 the_pcu.on_unblock_ack(&the_pcu);
Holger Hans Peter Freythera9744de2013-07-28 19:01:20 +0200279 bvc_timeout(NULL);
280 break;
281 case BSSGP_PDUT_SGSN_INVOKE_TRACE:
282 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
283 break;
284 default:
285 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
286 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
287 break;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400288 }
289 return rc;
290}
291
Holger Hans Peter Freyther90d5df42013-07-28 21:14:15 +0200292static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400293{
294 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
295 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
296 struct tlv_parsed tp;
297 uint8_t pdu_type = bgph->pdu_type;
298 uint16_t ns_bvci = msgb_bvci(msg);
299 int data_len;
300 int rc = 0;
Andreas Eversberg3e372d52012-07-06 09:28:15 +0200301 struct bssgp_bvc_ctx *bctx;
302
303 if (pdu_type == BSSGP_PDUT_STATUS) {
304 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u received STATUS\n",
305 msgb_nsei(msg), ns_bvci);
306 return 0;
307 }
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400308
309 /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
310
311 /* UNITDATA BSSGP headers have TLLI in front */
312 if (pdu_type != BSSGP_PDUT_UL_UNITDATA && pdu_type != BSSGP_PDUT_DL_UNITDATA)
313 {
314 data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
315 rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
316 }
317 else
318 {
319 data_len = msgb_bssgp_len(msg) - sizeof(*budh);
320 rc = bssgp_tlv_parse(&tp, budh->data, data_len);
321 }
322
323 /* look-up or create the BTS context for this BVC */
324 bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
325
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200326 if (!bctx
327 && pdu_type != BSSGP_PDUT_BVC_RESET_ACK
Andreas Eversberg8c3680d2012-10-08 12:30:56 +0200328 && pdu_type != BSSGP_PDUT_BVC_UNBLOCK_ACK
329 && pdu_type != BSSGP_PDUT_PAGING_PS)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400330 {
331 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
332 "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
333 pdu_type);
334 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
335 }
336
337 if (bctx)
338 {
339 log_set_context(BSC_CTX_BVC, bctx);
340 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
341 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], msgb_bssgp_len(msg));
342 }
343
344 if (ns_bvci == BVCI_SIGNALLING)
345 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200346 LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_SIGNALLING gprs_bssgp_rx_sign\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400347 rc = gprs_bssgp_pcu_rx_sign(msg, &tp, bctx);
348 }
349 else if (ns_bvci == BVCI_PTM)
350 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200351 LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTM bssgp_tx_status\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400352 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
353 }
354 else
355 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200356 LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTP gprs_bssgp_rx_ptp\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400357 rc = gprs_bssgp_pcu_rx_ptp(msg, &tp, bctx);
358 }
359 return rc;
360}
Harald Welte477e79e2012-06-18 12:21:03 +0800361
362int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
363{
364 return 0;
365}
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200366
367static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci)
368{
369 int rc = 0;
370 switch (event) {
371 case GPRS_NS_EVT_UNIT_DATA:
372 /* hand the message into the BSSGP implementation */
373 rc = gprs_bssgp_pcu_rcvmsg(msg);
374 break;
375 default:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200376 LOGP(DPCU, LOGL_NOTICE, "RLCMAC: Unknown event %u from NS\n", event);
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200377 rc = -EIO;
378 break;
379 }
380 return rc;
381}
382
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200383
384static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
385 void *handler_data, void *signal_data)
386{
387 struct ns_signal_data *nssd;
388
389 if (subsys != SS_L_NS)
390 return -EINVAL;
391
392 nssd = (struct ns_signal_data *)signal_data;
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200393 if (nssd->nsvc != the_pcu.nsvc) {
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200394 LOGP(DPCU, LOGL_ERROR, "Signal received of unknown NSVC\n");
395 return -EINVAL;
396 }
397
398 switch (signal) {
399 case S_NS_UNBLOCK:
Holger Hans Peter Freyther4b984b12013-07-13 12:57:06 +0200400 if (!the_pcu.nsvc_unblocked) {
401 the_pcu.nsvc_unblocked = 1;
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200402 LOGP(DPCU, LOGL_NOTICE, "NS-VC %d is unblocked.\n",
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200403 the_pcu.nsvc->nsvci);
404 the_pcu.bvc_sig_reset = 0;
405 the_pcu.bvc_reset = 0;
406 the_pcu.bvc_unblocked = 0;
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200407 bvc_timeout(NULL);
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200408 }
409 break;
410 case S_NS_BLOCK:
Holger Hans Peter Freyther4b984b12013-07-13 12:57:06 +0200411 if (the_pcu.nsvc_unblocked) {
412 the_pcu.nsvc_unblocked = 0;
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200413 osmo_timer_del(&the_pcu.bvc_timer);
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200414 the_pcu.bvc_sig_reset = 0;
415 the_pcu.bvc_reset = 0;
416 the_pcu.bvc_unblocked = 0;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200417 LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
418 }
419 break;
420 }
421
422 return 0;
423}
424
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200425int gprs_bssgp_tx_fc_bvc(void)
426{
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200427 if (!the_pcu.bctx) {
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200428 LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
429 return -EIO;
430 }
431 /* FIXME: use real values */
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200432 return bssgp_tx_fc_bvc(the_pcu.bctx, 1, 6553500, 819100, 50000, 50000,
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200433 NULL, NULL);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200434}
435
436static void bvc_timeout(void *_priv)
437{
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200438 if (!the_pcu.bvc_sig_reset) {
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200439 LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI 0\n");
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200440 bssgp_tx_bvc_reset(the_pcu.bctx, 0, BSSGP_CAUSE_OML_INTERV);
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200441 osmo_timer_schedule(&the_pcu.bvc_timer, 1, 0);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200442 return;
443 }
444
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200445 if (!the_pcu.bvc_reset) {
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200446 LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI %d\n",
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200447 the_pcu.bctx->bvci);
448 bssgp_tx_bvc_reset(the_pcu.bctx, the_pcu.bctx->bvci, BSSGP_CAUSE_OML_INTERV);
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200449 osmo_timer_schedule(&the_pcu.bvc_timer, 1, 0);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200450 return;
451 }
452
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200453 if (!the_pcu.bvc_unblocked) {
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200454 LOGP(DBSSGP, LOGL_INFO, "Sending unblock on BVCI %d\n",
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200455 the_pcu.bctx->bvci);
456 bssgp_tx_bvc_unblock(the_pcu.bctx);
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200457 osmo_timer_schedule(&the_pcu.bvc_timer, 1, 0);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200458 return;
459 }
460
461 LOGP(DBSSGP, LOGL_DEBUG, "Sending flow control info on BVCI %d\n",
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200462 the_pcu.bctx->bvci);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200463 gprs_bssgp_tx_fc_bvc();
Holger Hans Peter Freytherb67a8a32013-07-28 18:55:14 +0200464 osmo_timer_schedule(&the_pcu.bvc_timer, the_pcu.bts->fc_interval, 0);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200465}
466
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200467/* create BSSGP/NS layer instances */
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200468struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts,
Holger Hans Peter Freytherb67a8a32013-07-28 18:55:14 +0200469 uint16_t local_port, uint32_t sgsn_ip,
Harald Welte30a73d82013-03-10 08:54:30 +0000470 uint16_t sgsn_port, uint16_t nsei, uint16_t nsvci, uint16_t bvci,
471 uint16_t mcc, uint16_t mnc, uint16_t lac, uint16_t rac,
472 uint16_t cell_id)
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200473{
474 struct sockaddr_in dest;
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200475 int rc;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200476
Andreas Eversberg514491d2012-09-23 06:42:07 +0200477 mcc = ((mcc & 0xf00) >> 8) * 100 + ((mcc & 0x0f0) >> 4) * 10 + (mcc & 0x00f);
478 mnc = ((mnc & 0xf00) >> 8) * 100 + ((mnc & 0x0f0) >> 4) * 10 + (mnc & 0x00f);
479 cell_id = ntohs(cell_id);
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200480
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200481 /* if already created... return the current address */
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200482 if (the_pcu.bctx)
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200483 return &the_pcu;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200484
Holger Hans Peter Freytherb67a8a32013-07-28 18:55:14 +0200485 the_pcu.bts = bts;
486
Andreas Eversberg71cce912013-01-16 13:49:00 +0100487 bssgp_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_pcu_ctx);
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200488 if (!bssgp_nsi) {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200489 LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200490 return NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200491 }
Harald Weltede5253a2013-01-11 09:45:17 +0100492 gprs_ns_vty_init(bssgp_nsi);
Harald Welte30a73d82013-03-10 08:54:30 +0000493 bssgp_nsi->nsip.local_port = local_port;
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200494 rc = gprs_ns_nsip_listen(bssgp_nsi);
495 if (rc < 0) {
496 LOGP(DBSSGP, LOGL_ERROR, "Failed to create socket\n");
497 gprs_ns_destroy(bssgp_nsi);
498 bssgp_nsi = NULL;
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200499 return NULL;
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200500 }
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200501
502 dest.sin_family = AF_INET;
503 dest.sin_port = htons(sgsn_port);
504 dest.sin_addr.s_addr = htonl(sgsn_ip);
505
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200506 the_pcu.nsvc = gprs_ns_nsip_connect(bssgp_nsi, &dest, nsei, nsvci);
507 if (!the_pcu.nsvc) {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200508 LOGP(DBSSGP, LOGL_ERROR, "Failed to create NSVCt\n");
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200509 gprs_ns_destroy(bssgp_nsi);
510 bssgp_nsi = NULL;
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200511 return NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200512 }
513
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200514 the_pcu.bctx = btsctx_alloc(bvci, nsei);
515 if (!the_pcu.bctx) {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200516 LOGP(DBSSGP, LOGL_ERROR, "Failed to create BSSGP context\n");
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200517 the_pcu.nsvc = NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200518 gprs_ns_destroy(bssgp_nsi);
519 bssgp_nsi = NULL;
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200520 return NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200521 }
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200522 the_pcu.bctx->ra_id.mcc = spoof_mcc ? : mcc;
523 the_pcu.bctx->ra_id.mnc = spoof_mnc ? : mnc;
524 the_pcu.bctx->ra_id.lac = lac;
525 the_pcu.bctx->ra_id.rac = rac;
526 the_pcu.bctx->cell_id = cell_id;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200527
528 osmo_signal_register_handler(SS_L_NS, nsvc_signal_cb, NULL);
529
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200530 the_pcu.bvc_timer.cb = bvc_timeout;
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200531
532
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200533 return &the_pcu;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200534}
535
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200536void gprs_bssgp_destroy_or_exit(void)
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200537{
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200538 if (the_pcu.exit_on_destroy) {
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200539 LOGP(DBSSGP, LOGL_NOTICE, "Exiting on BSSGP destruction.\n");
540 exit(0);
541 }
542
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200543 if (!bssgp_nsi)
544 return;
545
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200546 osmo_timer_del(&the_pcu.bvc_timer);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200547
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200548 osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
549
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200550 the_pcu.nsvc = NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200551
552 /* FIXME: move this to libgb: btsctx_free() */
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200553 llist_del(&the_pcu.bctx->list);
554 talloc_free(the_pcu.bctx);
555 the_pcu.bctx = NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200556
557 /* FIXME: blocking... */
Holger Hans Peter Freyther40bd0c42013-07-13 12:58:20 +0200558 the_pcu.nsvc_unblocked = 0;
559 the_pcu.bvc_sig_reset = 0;
560 the_pcu.bvc_reset = 0;
561 the_pcu.bvc_unblocked = 0;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200562
563 gprs_ns_destroy(bssgp_nsi);
564 bssgp_nsi = NULL;
565}
566
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200567void gprs_bssgp_exit_on_destroy(void)
568{
569 LOGP(DBSSGP, LOGL_NOTICE, "Going to quit on BSSGP destruction\n");
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200570 the_pcu.exit_on_destroy = 1;
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200571}
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200572
573struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void)
574{
575 return the_pcu.bctx;
576}