blob: d8ba09c719c36f99558a745bf9d1493dde0a1856 [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 Freyther67ed34e2013-10-17 17:01:54 +020024#include <bts.h>
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +020025#include <tbf.h>
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040026
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +020027static struct gprs_bssgp_pcu the_pcu = { 0, };
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +020028
Andreas Eversberg71cce912013-01-16 13:49:00 +010029extern void *tall_pcu_ctx;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +020030extern uint16_t spoof_mcc, spoof_mnc;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040031
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +020032static void bvc_timeout(void *_priv);
33
Andreas Eversberg00950742012-10-08 12:25:55 +020034static int parse_imsi(struct tlv_parsed *tp, char *imsi)
35{
36 uint8_t imsi_len;
37 uint8_t *bcd_imsi;
38 int i, j;
39
40 if (!TLVP_PRESENT(tp, BSSGP_IE_IMSI))
41 return -EINVAL;
42
43 imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
44 bcd_imsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_IMSI);
45
46 if ((bcd_imsi[0] & 0x08))
47 imsi_len = imsi_len * 2 - 1;
48 else
49 imsi_len = (imsi_len - 1) * 2;
50 for (i = 0, j = 0; j < imsi_len && j < 15; j++)
51 {
52 if (!(j & 1)) {
53 imsi[j] = (bcd_imsi[i] >> 4) + '0';
54 i++;
55 } else
56 imsi[j] = (bcd_imsi[i] & 0xf) + '0';
57 }
58 imsi[j] = '\0';
59
60 return 0;
61}
62
Holger Hans Peter Freyther98fe9452013-07-13 15:23:58 +020063static int parse_ra_cap_ms_class(struct tlv_parsed *tp)
64{
65 bitvec *block;
66 unsigned rp = 0;
67 uint8_t ms_class = 0;
68 uint8_t cap_len;
69 uint8_t *cap;
70
71 if (!TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
72 return ms_class;
73
74 cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
75 cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
76
77 block = bitvec_alloc(cap_len);
78 bitvec_unpack(block, cap);
79 bitvec_read_field(block, rp, 4); // Access Technology Type
80 bitvec_read_field(block, rp, 7); // Length of Access Capabilities
81 bitvec_read_field(block, rp, 3); // RF Power Capability
82 if (bitvec_read_field(block, rp, 1)) // A5 Bits Present
83 bitvec_read_field(block, rp, 7); // A5 Bits
84 bitvec_read_field(block, rp, 1); // ES IND
85 bitvec_read_field(block, rp, 1); // PS
86 bitvec_read_field(block, rp, 1); // VGCS
87 bitvec_read_field(block, rp, 1); // VBS
88 if (bitvec_read_field(block, rp, 1)) { // Multislot Cap Present
89 if (bitvec_read_field(block, rp, 1)) // HSCSD Present
90 bitvec_read_field(block, rp, 5); // Class
91 if (bitvec_read_field(block, rp, 1)) { // GPRS Present
92 ms_class = bitvec_read_field(block, rp, 5); // Class
93 bitvec_read_field(block, rp, 1); // Ext.
94 }
95 if (bitvec_read_field(block, rp, 1)) // SMS Present
96 bitvec_read_field(block, rp, 4); // SMS Value
97 bitvec_read_field(block, rp, 4); // SMS Value
98 }
99
100 bitvec_free(block);
101 return ms_class;
102}
103
Holger Hans Peter Freyther90d5df42013-07-28 21:14:15 +0200104static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400105{
106 struct bssgp_ud_hdr *budh;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400107
Andreas Eversberge6228b32012-07-03 13:36:03 +0200108 uint32_t tlli;
Andreas Eversberge6228b32012-07-03 13:36:03 +0200109 uint8_t *data;
110 uint16_t len;
Andreas Eversberg00950742012-10-08 12:25:55 +0200111 char imsi[16] = "000";
Andreas Eversberg7d7cf542012-06-25 09:26:15 +0200112
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400113 budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
Andreas Eversberge6228b32012-07-03 13:36:03 +0200114 tlli = ntohl(budh->tlli);
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400115
Ivan Kluchnikov5e0df932012-06-12 15:33:52 +0400116 /* LLC_PDU is mandatory IE */
117 if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400118 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200119 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD missing mandatory IE\n", tlli);
Ivan Kluchnikov5e0df932012-06-12 15:33:52 +0400120 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
121 }
Ivan Kluchnikovb172b1b2012-06-07 01:51:49 +0400122
Andreas Eversberge6228b32012-07-03 13:36:03 +0200123 data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
124 len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +0200125 if (len > sizeof(gprs_rlcmac_tbf::llc_frame))
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400126 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200127 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 +0200128 return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400129 }
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400130
Andreas Eversberge13fa2d2012-07-09 17:10:44 +0200131 /* read IMSI. if no IMSI exists, use first paging block (any paging),
132 * because during attachment the IMSI might not be known, so the MS
133 * will listen to all paging blocks. */
Andreas Eversberg00950742012-10-08 12:25:55 +0200134 parse_imsi(tp, imsi);
Andreas Eversberg51ab1342012-07-13 14:52:50 +0200135
136 /* parse ms radio access capability */
Holger Hans Peter Freyther98fe9452013-07-13 15:23:58 +0200137 uint8_t ms_class = parse_ra_cap_ms_class(tp);
Andreas Eversberg51ab1342012-07-13 14:52:50 +0200138
Andreas Eversberg24131bf2012-07-21 11:09:58 +0200139 /* get lifetime */
140 uint16_t delay_csec = 0xffff;
141 if (TLVP_PRESENT(tp, BSSGP_IE_PDU_LIFETIME))
142 {
143 uint8_t lt_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
144 uint16_t *lt = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_PDU_LIFETIME);
145 if (lt_len == 2)
146 delay_csec = ntohs(*lt);
147 else
148 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
149 "PDU_LIFETIME IE\n");
150 } else
151 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP missing mandatory "
152 "PDU_LIFETIME IE\n");
153
Andreas Eversberge13fa2d2012-07-09 17:10:44 +0200154 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 +0400155
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +0200156 return tbf_handle(the_pcu.bts, tlli, imsi, ms_class, delay_csec, data, len);
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400157}
Ivan Kluchnikova9e6dc52012-06-17 08:30:06 +0400158
Andreas Eversberg8c3680d2012-10-08 12:30:56 +0200159int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
160{
161 char imsi[16];
162 uint8_t *ptmsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_TMSI);
163 uint16_t ptmsi_len = TLVP_LEN(tp, BSSGP_IE_TMSI);
164
165 LOGP(DBSSGP, LOGL_NOTICE, " P-TMSI = ");
166 for (int i = 0; i < ptmsi_len; i++)
167 {
168 LOGPC(DBSSGP, LOGL_NOTICE, "%02x", ptmsi[i]);
169 }
170 LOGPC(DBSSGP, LOGL_NOTICE, "\n");
171
172 if (parse_imsi(tp, imsi))
173 {
174 LOGP(DBSSGP, LOGL_ERROR, "No IMSI\n");
175 return -EINVAL;
176 }
177
178 return gprs_rlcmac_paging_request(ptmsi, ptmsi_len, imsi);
179}
180
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400181/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
Holger Hans Peter Freyther90d5df42013-07-28 21:14:15 +0200182static 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 +0400183{
184 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
185 uint8_t pdu_type = bgph->pdu_type;
186 unsigned rc = 0;
187
Andreas Eversbergba1cd9b2012-07-25 09:14:09 +0200188 if (!bctx)
189 return -EINVAL;
190
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400191 /* If traffic is received on a BVC that is marked as blocked, the
192 * received PDU shall not be accepted and a STATUS PDU (Cause value:
193 * BVC Blocked) shall be sent to the peer entity on the signalling BVC */
194 if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS)
195 {
196 uint16_t bvci = msgb_bvci(msg);
197 LOGP(DBSSGP, LOGL_NOTICE, "rx BVC_S_BLOCKED\n");
198 return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
199 }
200
201 switch (pdu_type) {
202 case BSSGP_PDUT_DL_UNITDATA:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200203 LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
Holger Hans Peter Freyther416ce692013-08-22 08:44:04 +0200204 if (the_pcu.on_dl_unit_data)
205 the_pcu.on_dl_unit_data(&the_pcu, msg, tp);
Ivan Kluchnikov5e0df932012-06-12 15:33:52 +0400206 gprs_bssgp_pcu_rx_dl_ud(msg, tp);
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400207 break;
208 case BSSGP_PDUT_PAGING_PS:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200209 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400210 break;
211 case BSSGP_PDUT_PAGING_CS:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200212 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400213 break;
214 case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200215 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400216 break;
217 case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200218 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400219 break;
220 case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200221 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400222 break;
223 default:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200224 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400225 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
226 break;
227 }
228 return rc;
229}
230
231/* Receive a BSSGP PDU from a SGSN on a SIGNALLING BVCI */
Holger Hans Peter Freyther90d5df42013-07-28 21:14:15 +0200232static 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 +0400233{
234 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
235 int rc = 0;
236 switch (bgph->pdu_type) {
237 case BSSGP_PDUT_STATUS:
238 /* Some exception has occurred */
239 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
240 /* FIXME: send NM_STATUS.ind to NM */
241 break;
Holger Hans Peter Freythera9744de2013-07-28 19:01:20 +0200242 case BSSGP_PDUT_SUSPEND_ACK:
243 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
244 break;
245 case BSSGP_PDUT_SUSPEND_NACK:
246 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
247 break;
248 case BSSGP_PDUT_BVC_RESET_ACK:
249 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
250 if (!the_pcu.bvc_sig_reset)
251 the_pcu.bvc_sig_reset = 1;
252 else
253 the_pcu.bvc_reset = 1;
254 bvc_timeout(NULL);
255 break;
256 case BSSGP_PDUT_PAGING_PS:
257 LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_PAGING_PS\n");
258 gprs_bssgp_pcu_rx_paging_ps(msg, tp);
259 break;
260 case BSSGP_PDUT_PAGING_CS:
261 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
262 break;
263 case BSSGP_PDUT_RESUME_ACK:
264 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
265 break;
266 case BSSGP_PDUT_RESUME_NACK:
267 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
268 break;
269 case BSSGP_PDUT_FLUSH_LL:
270 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
271 break;
272 case BSSGP_PDUT_BVC_BLOCK_ACK:
273 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
274 break;
275 case BSSGP_PDUT_BVC_UNBLOCK_ACK:
276 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
277 the_pcu.bvc_unblocked = 1;
Holger Hans Peter Freytherc0f16442013-08-22 08:40:33 +0200278 if (the_pcu.on_unblock_ack)
279 the_pcu.on_unblock_ack(&the_pcu);
Holger Hans Peter Freythera9744de2013-07-28 19:01:20 +0200280 bvc_timeout(NULL);
281 break;
282 case BSSGP_PDUT_SGSN_INVOKE_TRACE:
283 LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
284 break;
285 default:
286 LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
287 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
288 break;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400289 }
290 return rc;
291}
292
Holger Hans Peter Freyther90d5df42013-07-28 21:14:15 +0200293static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400294{
295 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
296 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
297 struct tlv_parsed tp;
298 uint8_t pdu_type = bgph->pdu_type;
299 uint16_t ns_bvci = msgb_bvci(msg);
300 int data_len;
301 int rc = 0;
Andreas Eversberg3e372d52012-07-06 09:28:15 +0200302 struct bssgp_bvc_ctx *bctx;
303
304 if (pdu_type == BSSGP_PDUT_STATUS) {
305 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u received STATUS\n",
306 msgb_nsei(msg), ns_bvci);
307 return 0;
308 }
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400309
310 /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
311
312 /* UNITDATA BSSGP headers have TLLI in front */
313 if (pdu_type != BSSGP_PDUT_UL_UNITDATA && pdu_type != BSSGP_PDUT_DL_UNITDATA)
314 {
315 data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
316 rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
317 }
318 else
319 {
320 data_len = msgb_bssgp_len(msg) - sizeof(*budh);
321 rc = bssgp_tlv_parse(&tp, budh->data, data_len);
322 }
323
324 /* look-up or create the BTS context for this BVC */
325 bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
326
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200327 if (!bctx
328 && pdu_type != BSSGP_PDUT_BVC_RESET_ACK
Andreas Eversberg8c3680d2012-10-08 12:30:56 +0200329 && pdu_type != BSSGP_PDUT_BVC_UNBLOCK_ACK
330 && pdu_type != BSSGP_PDUT_PAGING_PS)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400331 {
332 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
333 "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
334 pdu_type);
335 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
336 }
337
338 if (bctx)
339 {
340 log_set_context(BSC_CTX_BVC, bctx);
341 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
342 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], msgb_bssgp_len(msg));
343 }
344
345 if (ns_bvci == BVCI_SIGNALLING)
346 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200347 LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_SIGNALLING gprs_bssgp_rx_sign\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400348 rc = gprs_bssgp_pcu_rx_sign(msg, &tp, bctx);
349 }
350 else if (ns_bvci == BVCI_PTM)
351 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200352 LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTM bssgp_tx_status\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400353 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
354 }
355 else
356 {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200357 LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTP gprs_bssgp_rx_ptp\n");
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400358 rc = gprs_bssgp_pcu_rx_ptp(msg, &tp, bctx);
359 }
360 return rc;
361}
Harald Welte477e79e2012-06-18 12:21:03 +0800362
363int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
364{
365 return 0;
366}
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200367
368static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci)
369{
370 int rc = 0;
371 switch (event) {
372 case GPRS_NS_EVT_UNIT_DATA:
373 /* hand the message into the BSSGP implementation */
374 rc = gprs_bssgp_pcu_rcvmsg(msg);
375 break;
376 default:
Andreas Eversberg0e403092012-07-06 11:04:57 +0200377 LOGP(DPCU, LOGL_NOTICE, "RLCMAC: Unknown event %u from NS\n", event);
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200378 rc = -EIO;
379 break;
380 }
381 return rc;
382}
383
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200384
385static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
386 void *handler_data, void *signal_data)
387{
388 struct ns_signal_data *nssd;
389
390 if (subsys != SS_L_NS)
391 return -EINVAL;
392
393 nssd = (struct ns_signal_data *)signal_data;
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200394 if (nssd->nsvc != the_pcu.nsvc) {
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200395 LOGP(DPCU, LOGL_ERROR, "Signal received of unknown NSVC\n");
396 return -EINVAL;
397 }
398
399 switch (signal) {
400 case S_NS_UNBLOCK:
Holger Hans Peter Freyther4b984b12013-07-13 12:57:06 +0200401 if (!the_pcu.nsvc_unblocked) {
402 the_pcu.nsvc_unblocked = 1;
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200403 LOGP(DPCU, LOGL_NOTICE, "NS-VC %d is unblocked.\n",
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200404 the_pcu.nsvc->nsvci);
405 the_pcu.bvc_sig_reset = 0;
406 the_pcu.bvc_reset = 0;
407 the_pcu.bvc_unblocked = 0;
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200408 bvc_timeout(NULL);
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200409 }
410 break;
411 case S_NS_BLOCK:
Holger Hans Peter Freyther4b984b12013-07-13 12:57:06 +0200412 if (the_pcu.nsvc_unblocked) {
413 the_pcu.nsvc_unblocked = 0;
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200414 osmo_timer_del(&the_pcu.bvc_timer);
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200415 the_pcu.bvc_sig_reset = 0;
416 the_pcu.bvc_reset = 0;
417 the_pcu.bvc_unblocked = 0;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200418 LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
419 }
420 break;
421 }
422
423 return 0;
424}
425
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200426int gprs_bssgp_tx_fc_bvc(void)
427{
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200428 if (!the_pcu.bctx) {
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200429 LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
430 return -EIO;
431 }
432 /* FIXME: use real values */
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200433 return bssgp_tx_fc_bvc(the_pcu.bctx, 1, 6553500, 819100, 50000, 50000,
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200434 NULL, NULL);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200435}
436
437static void bvc_timeout(void *_priv)
438{
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200439 if (!the_pcu.bvc_sig_reset) {
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200440 LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI 0\n");
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200441 bssgp_tx_bvc_reset(the_pcu.bctx, 0, BSSGP_CAUSE_OML_INTERV);
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200442 osmo_timer_schedule(&the_pcu.bvc_timer, 1, 0);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200443 return;
444 }
445
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200446 if (!the_pcu.bvc_reset) {
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200447 LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI %d\n",
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200448 the_pcu.bctx->bvci);
449 bssgp_tx_bvc_reset(the_pcu.bctx, the_pcu.bctx->bvci, BSSGP_CAUSE_OML_INTERV);
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200450 osmo_timer_schedule(&the_pcu.bvc_timer, 1, 0);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200451 return;
452 }
453
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200454 if (!the_pcu.bvc_unblocked) {
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200455 LOGP(DBSSGP, LOGL_INFO, "Sending unblock on BVCI %d\n",
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200456 the_pcu.bctx->bvci);
457 bssgp_tx_bvc_unblock(the_pcu.bctx);
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200458 osmo_timer_schedule(&the_pcu.bvc_timer, 1, 0);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200459 return;
460 }
461
462 LOGP(DBSSGP, LOGL_DEBUG, "Sending flow control info on BVCI %d\n",
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200463 the_pcu.bctx->bvci);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200464 gprs_bssgp_tx_fc_bvc();
Holger Hans Peter Freytherb67a8a32013-07-28 18:55:14 +0200465 osmo_timer_schedule(&the_pcu.bvc_timer, the_pcu.bts->fc_interval, 0);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200466}
467
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200468/* create BSSGP/NS layer instances */
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200469struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts,
Holger Hans Peter Freytherb67a8a32013-07-28 18:55:14 +0200470 uint16_t local_port, uint32_t sgsn_ip,
Harald Welte30a73d82013-03-10 08:54:30 +0000471 uint16_t sgsn_port, uint16_t nsei, uint16_t nsvci, uint16_t bvci,
472 uint16_t mcc, uint16_t mnc, uint16_t lac, uint16_t rac,
473 uint16_t cell_id)
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200474{
475 struct sockaddr_in dest;
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200476 int rc;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200477
Andreas Eversberg514491d2012-09-23 06:42:07 +0200478 mcc = ((mcc & 0xf00) >> 8) * 100 + ((mcc & 0x0f0) >> 4) * 10 + (mcc & 0x00f);
479 mnc = ((mnc & 0xf00) >> 8) * 100 + ((mnc & 0x0f0) >> 4) * 10 + (mnc & 0x00f);
480 cell_id = ntohs(cell_id);
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200481
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200482 /* if already created... return the current address */
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200483 if (the_pcu.bctx)
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200484 return &the_pcu;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200485
Holger Hans Peter Freytherb67a8a32013-07-28 18:55:14 +0200486 the_pcu.bts = bts;
487
Andreas Eversberg71cce912013-01-16 13:49:00 +0100488 bssgp_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_pcu_ctx);
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200489 if (!bssgp_nsi) {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200490 LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200491 return NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200492 }
Harald Weltede5253a2013-01-11 09:45:17 +0100493 gprs_ns_vty_init(bssgp_nsi);
Harald Welte30a73d82013-03-10 08:54:30 +0000494 bssgp_nsi->nsip.local_port = local_port;
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200495 rc = gprs_ns_nsip_listen(bssgp_nsi);
496 if (rc < 0) {
497 LOGP(DBSSGP, LOGL_ERROR, "Failed to create socket\n");
498 gprs_ns_destroy(bssgp_nsi);
499 bssgp_nsi = NULL;
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200500 return NULL;
Andreas Eversberga3c12fb2012-09-28 22:46:33 +0200501 }
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200502
503 dest.sin_family = AF_INET;
504 dest.sin_port = htons(sgsn_port);
505 dest.sin_addr.s_addr = htonl(sgsn_ip);
506
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200507 the_pcu.nsvc = gprs_ns_nsip_connect(bssgp_nsi, &dest, nsei, nsvci);
508 if (!the_pcu.nsvc) {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200509 LOGP(DBSSGP, LOGL_ERROR, "Failed to create NSVCt\n");
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200510 gprs_ns_destroy(bssgp_nsi);
511 bssgp_nsi = NULL;
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200512 return NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200513 }
514
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200515 the_pcu.bctx = btsctx_alloc(bvci, nsei);
516 if (!the_pcu.bctx) {
Andreas Eversberg0e403092012-07-06 11:04:57 +0200517 LOGP(DBSSGP, LOGL_ERROR, "Failed to create BSSGP context\n");
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200518 the_pcu.nsvc = NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200519 gprs_ns_destroy(bssgp_nsi);
520 bssgp_nsi = NULL;
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200521 return NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200522 }
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200523 the_pcu.bctx->ra_id.mcc = spoof_mcc ? : mcc;
524 the_pcu.bctx->ra_id.mnc = spoof_mnc ? : mnc;
525 the_pcu.bctx->ra_id.lac = lac;
526 the_pcu.bctx->ra_id.rac = rac;
527 the_pcu.bctx->cell_id = cell_id;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200528
529 osmo_signal_register_handler(SS_L_NS, nsvc_signal_cb, NULL);
530
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200531 the_pcu.bvc_timer.cb = bvc_timeout;
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200532
533
Holger Hans Peter Freythere8d9a5f2013-07-28 19:11:20 +0200534 return &the_pcu;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200535}
536
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200537void gprs_bssgp_destroy_or_exit(void)
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200538{
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200539 if (the_pcu.exit_on_destroy) {
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200540 LOGP(DBSSGP, LOGL_NOTICE, "Exiting on BSSGP destruction.\n");
541 exit(0);
542 }
543
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200544 if (!bssgp_nsi)
545 return;
546
Holger Hans Peter Freythered70cb72013-07-13 12:53:46 +0200547 osmo_timer_del(&the_pcu.bvc_timer);
Andreas Eversbergcd8a83a2012-09-23 06:41:21 +0200548
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200549 osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
550
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200551 the_pcu.nsvc = NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200552
553 /* FIXME: move this to libgb: btsctx_free() */
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200554 llist_del(&the_pcu.bctx->list);
555 talloc_free(the_pcu.bctx);
556 the_pcu.bctx = NULL;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200557
558 /* FIXME: blocking... */
Holger Hans Peter Freyther40bd0c42013-07-13 12:58:20 +0200559 the_pcu.nsvc_unblocked = 0;
560 the_pcu.bvc_sig_reset = 0;
561 the_pcu.bvc_reset = 0;
562 the_pcu.bvc_unblocked = 0;
Andreas Eversbergbf5a0f62012-07-06 08:58:22 +0200563
564 gprs_ns_destroy(bssgp_nsi);
565 bssgp_nsi = NULL;
566}
567
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200568void gprs_bssgp_exit_on_destroy(void)
569{
570 LOGP(DBSSGP, LOGL_NOTICE, "Going to quit on BSSGP destruction\n");
Holger Hans Peter Freyther90f08ef2013-07-13 12:45:44 +0200571 the_pcu.exit_on_destroy = 1;
Holger Hans Peter Freythera30f4762013-07-11 16:13:38 +0200572}
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200573
574struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void)
575{
576 return the_pcu.bctx;
577}