blob: 41605f4b0b7483434cbe223c965c07f98cc3da73 [file] [log] [blame]
Alexander Couzens6a161492020-07-12 13:45:50 +02001/*! \file gprs_ns2_message.c
2 * NS-over-FR-over-GRE implementation.
3 * GPRS Networks Service (NS) messages on the Gb interface.
4 * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
5 * as well as its successor 3GPP TS 48.016 */
6
7/* (C) 2020 sysmocom - s.f.m.c. GmbH
8 * Author: Alexander Couzens <lynxis@fe80.eu>
9 *
10 * All Rights Reserved
11 *
12 * SPDX-License-Identifier: GPL-2.0+
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 *
27 */
28
29#include <errno.h>
30
31#include <osmocom/core/byteswap.h>
32#include <osmocom/core/rate_ctr.h>
33#include <osmocom/core/stat_item.h>
34#include <osmocom/core/stats.h>
35#include <osmocom/gsm/tlv.h>
36#include <osmocom/gprs/gprs_msgb.h>
37#include <osmocom/gprs/gprs_ns2.h>
38#include <osmocom/gprs/protocol/gsm_08_16.h>
39
40#include "gprs_ns2_internal.h"
41
42#define ERR_IF_NSVC_USES_SNS(nsvc, reason) \
43 do { \
44 if (!nsvc->nse->bss_sns_fi) \
45 break; \
46 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Rx invalid packet %s with SNS\n", \
47 nsvc->nse->nsei, reason); \
48 } while (0)
49
50enum ns_ctr {
51 NS_CTR_PKTS_IN,
52 NS_CTR_PKTS_OUT,
53 NS_CTR_BYTES_IN,
54 NS_CTR_BYTES_OUT,
55 NS_CTR_BLOCKED,
56 NS_CTR_DEAD,
57 NS_CTR_REPLACED,
58 NS_CTR_NSEI_CHG,
59 NS_CTR_INV_VCI,
60 NS_CTR_INV_NSEI,
61 NS_CTR_LOST_ALIVE,
62 NS_CTR_LOST_RESET,
63};
64
65
66
Harald Welteb40cfdc2020-09-18 22:48:34 +020067static int gprs_ns2_validate_reset(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
Alexander Couzens6a161492020-07-12 13:45:50 +020068{
Harald Welte798efea2020-12-03 16:01:02 +010069 if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1) ||
70 !TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
Alexander Couzens6a161492020-07-12 13:45:50 +020071 *cause = NS_CAUSE_MISSING_ESSENT_IE;
72 return -1;
73 }
74
75 return 0;
76}
77
Harald Welteb40cfdc2020-09-18 22:48:34 +020078static int gprs_ns2_validate_reset_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
Alexander Couzens6a161492020-07-12 13:45:50 +020079{
Harald Welte798efea2020-12-03 16:01:02 +010080 if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
Alexander Couzens6a161492020-07-12 13:45:50 +020081 *cause = NS_CAUSE_MISSING_ESSENT_IE;
82 return -1;
83 }
84
85 return 0;
86}
87
Harald Welteb40cfdc2020-09-18 22:48:34 +020088static int gprs_ns2_validate_block(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
Alexander Couzens6a161492020-07-12 13:45:50 +020089{
Harald Welte798efea2020-12-03 16:01:02 +010090 if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
Alexander Couzens6a161492020-07-12 13:45:50 +020091 *cause = NS_CAUSE_MISSING_ESSENT_IE;
92 return -1;
93 }
94
95 return 0;
96}
97
Harald Welteb40cfdc2020-09-18 22:48:34 +020098static int gprs_ns2_validate_block_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
Alexander Couzens6a161492020-07-12 13:45:50 +020099{
Harald Welte798efea2020-12-03 16:01:02 +0100100 if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200101 *cause = NS_CAUSE_MISSING_ESSENT_IE;
102 return -1;
103 }
104
105 return 0;
106}
107
Harald Welteb40cfdc2020-09-18 22:48:34 +0200108static int gprs_ns2_validate_status(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
Alexander Couzens6a161492020-07-12 13:45:50 +0200109{
110
Harald Welte798efea2020-12-03 16:01:02 +0100111 if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200112 *cause = NS_CAUSE_MISSING_ESSENT_IE;
113 return -1;
114 }
115
116 uint8_t _cause = tlvp_val8(tp, NS_IE_VCI, 0);
117
118 switch (_cause) {
119 case NS_CAUSE_NSVC_BLOCKED:
120 case NS_CAUSE_NSVC_UNKNOWN:
Harald Welte798efea2020-12-03 16:01:02 +0100121 if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200122 *cause = NS_CAUSE_MISSING_ESSENT_IE;
123 return -1;
124 }
125 break;
126 case NS_CAUSE_SEM_INCORR_PDU:
127 case NS_CAUSE_PDU_INCOMP_PSTATE:
128 case NS_CAUSE_PROTO_ERR_UNSPEC:
129 case NS_CAUSE_INVAL_ESSENT_IE:
130 case NS_CAUSE_MISSING_ESSENT_IE:
Harald Welte798efea2020-12-03 16:01:02 +0100131 if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200132 *cause = NS_CAUSE_MISSING_ESSENT_IE;
133 return -1;
134 }
135 break;
136 case NS_CAUSE_BVCI_UNKNOWN:
Harald Welte798efea2020-12-03 16:01:02 +0100137 if (!TLVP_PRES_LEN(tp, NS_IE_BVCI, 2)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200138 *cause = NS_CAUSE_MISSING_ESSENT_IE;
139 return -1;
140 }
141 break;
142 case NS_CAUSE_UNKN_IP_TEST_FAILED:
Harald Welte798efea2020-12-03 16:01:02 +0100143 if (!TLVP_PRESENT(tp, NS_IE_IPv4_LIST) && !TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200144 *cause = NS_CAUSE_MISSING_ESSENT_IE;
145 return -1;
146 }
147 break;
148 }
149
150 return 0;
151}
152
153int gprs_ns2_validate(struct gprs_ns2_vc *nsvc,
154 uint8_t pdu_type,
155 struct msgb *msg,
156 struct tlv_parsed *tp,
157 uint8_t *cause)
158{
159 switch (pdu_type) {
160 case NS_PDUT_RESET:
161 return gprs_ns2_validate_reset(nsvc, msg, tp, cause);
162 case NS_PDUT_RESET_ACK:
163 return gprs_ns2_validate_reset_ack(nsvc, msg, tp, cause);
164 case NS_PDUT_BLOCK:
165 return gprs_ns2_validate_block(nsvc, msg, tp, cause);
166 case NS_PDUT_BLOCK_ACK:
167 return gprs_ns2_validate_block_ack(nsvc, msg, tp, cause);
168 case NS_PDUT_STATUS:
169 return gprs_ns2_validate_status(nsvc, msg, tp, cause);
170
171 /* following PDUs doesn't have any payloads */
172 case NS_PDUT_ALIVE:
173 case NS_PDUT_ALIVE_ACK:
174 case NS_PDUT_UNBLOCK:
175 case NS_PDUT_UNBLOCK_ACK:
176 if (msgb_l2len(msg) != sizeof(struct gprs_ns_hdr)) {
177 *cause = NS_CAUSE_PROTO_ERR_UNSPEC;
178 return -1;
179 }
180 break;
181 }
182
183 return 0;
184}
185
186
187/* transmit functions */
188static int ns2_tx_simple(struct gprs_ns2_vc *nsvc, uint8_t pdu_type)
189{
190 struct msgb *msg = gprs_ns2_msgb_alloc();
191 struct gprs_ns_hdr *nsh;
192
Daniel Willmann751977b2020-12-02 18:59:44 +0100193 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200194 log_set_context(LOG_CTX_GB_NSVC, nsvc);
195
196 if (!msg)
197 return -ENOMEM;
198
199 msg->l2h = msgb_put(msg, sizeof(*nsh));
200 nsh = (struct gprs_ns_hdr *) msg->l2h;
201
202 nsh->pdu_type = pdu_type;
203
204 return nsvc->bind->send_vc(nsvc, msg);
205}
206
Harald Welte5bef2cc2020-09-18 22:33:24 +0200207/*! Transmit a NS-BLOCK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200208 * \param[in] vc NS-VC on which the NS-BLOCK is to be transmitted
209 * \param[in] cause Numeric NS Cause value
Harald Welte5bef2cc2020-09-18 22:33:24 +0200210 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200211int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause)
212{
213 struct msgb *msg;
214 struct gprs_ns_hdr *nsh;
215 uint16_t nsvci = osmo_htons(nsvc->nsvci);
216
Daniel Willmann751977b2020-12-02 18:59:44 +0100217 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200218 log_set_context(LOG_CTX_GB_NSVC, nsvc);
219
220 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK");
221
222 msg = gprs_ns2_msgb_alloc();
223 if (!msg)
224 return -ENOMEM;
225
226 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n",
227 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
228
229 rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
230
231 msg->l2h = msgb_put(msg, sizeof(*nsh));
232 nsh = (struct gprs_ns_hdr *) msg->l2h;
233 nsh->pdu_type = NS_PDUT_BLOCK;
234
235 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
236 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
237
238 return nsvc->bind->send_vc(nsvc, msg);
239}
240
Harald Welte5bef2cc2020-09-18 22:33:24 +0200241/*! Transmit a NS-BLOCK-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200242 * \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200243 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200244int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc)
245{
246 struct msgb *msg;
247 struct gprs_ns_hdr *nsh;
248 uint16_t nsvci = osmo_htons(nsvc->nsvci);
249
Daniel Willmann751977b2020-12-02 18:59:44 +0100250 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200251 log_set_context(LOG_CTX_GB_NSVC, nsvc);
252
253 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK ACK");
254
255 msg = gprs_ns2_msgb_alloc();
256 if (!msg)
257 return -ENOMEM;
258
259 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK ACK (NSVCI=%u)\n", nsvc->nse->nsei, nsvc->nsvci);
260
Alexander Couzens6a161492020-07-12 13:45:50 +0200261 msg->l2h = msgb_put(msg, sizeof(*nsh));
262 nsh = (struct gprs_ns_hdr *) msg->l2h;
263 nsh->pdu_type = NS_PDUT_BLOCK_ACK;
264
265 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
266
267 return nsvc->bind->send_vc(nsvc, msg);
268}
269
Harald Welte5bef2cc2020-09-18 22:33:24 +0200270/*! Transmit a NS-RESET on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200271 * \param[in] nsvc NS-VC used for transmission
272 * \paam[in] cause Numeric NS cause value
Harald Welte5bef2cc2020-09-18 22:33:24 +0200273 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200274int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
275{
276 struct msgb *msg;
277 struct gprs_ns_hdr *nsh;
278 uint16_t nsvci = osmo_htons(nsvc->nsvci);
279 uint16_t nsei = osmo_htons(nsvc->nse->nsei);
280
Daniel Willmann751977b2020-12-02 18:59:44 +0100281 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200282 log_set_context(LOG_CTX_GB_NSVC, nsvc);
283
284 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
285
286 msg = gprs_ns2_msgb_alloc();
287 if (!msg)
288 return -ENOMEM;
289
290 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n",
291 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
292
293 msg->l2h = msgb_put(msg, sizeof(*nsh));
294 nsh = (struct gprs_ns_hdr *) msg->l2h;
295 nsh->pdu_type = NS_PDUT_RESET;
296
297 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
298 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
299 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei);
300
301 return nsvc->bind->send_vc(nsvc, msg);
302}
303
Harald Welte5bef2cc2020-09-18 22:33:24 +0200304/*! Transmit a NS-RESET-ACK on a given NS-VC.
305 * \param[in] nsvc NS-VC used for transmission
306 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200307int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc)
308{
309 struct msgb *msg;
310 struct gprs_ns_hdr *nsh;
311 uint16_t nsvci, nsei;
312
Harald Welte5bef2cc2020-09-18 22:33:24 +0200313 /* Section 9.2.6 */
Daniel Willmann751977b2020-12-02 18:59:44 +0100314 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200315 log_set_context(LOG_CTX_GB_NSVC, nsvc);
316
317 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
318
319 msg = gprs_ns2_msgb_alloc();
320 if (!msg)
321 return -ENOMEM;
322
323 nsvci = osmo_htons(nsvc->nsvci);
324 nsei = osmo_htons(nsvc->nse->nsei);
325
326 msg->l2h = msgb_put(msg, sizeof(*nsh));
327 nsh = (struct gprs_ns_hdr *) msg->l2h;
328
329 nsh->pdu_type = NS_PDUT_RESET_ACK;
330
331 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n",
332 nsvc->nse->nsei, nsvc->nsvci);
333
334 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
335 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
336
337 return nsvc->bind->send_vc(nsvc, msg);
338}
339
Harald Welte5bef2cc2020-09-18 22:33:24 +0200340/*! Transmit a NS-UNBLOCK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200341 * \param[in] nsvc NS-VC on which the NS-UNBLOCK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200342 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200343int ns2_tx_unblock(struct gprs_ns2_vc *nsvc)
344{
Daniel Willmann751977b2020-12-02 18:59:44 +0100345 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200346 log_set_context(LOG_CTX_GB_NSVC, nsvc);
347
348 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK");
349
350 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
351 nsvc->nse->nsei, nsvc->nsvci);
352
353 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK);
354}
355
356
Harald Welte5bef2cc2020-09-18 22:33:24 +0200357/*! Transmit a NS-UNBLOCK-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200358 * \param[in] nsvc NS-VC on which the NS-UNBLOCK-ACK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200359 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200360int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc)
361{
Daniel Willmann751977b2020-12-02 18:59:44 +0100362 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200363 log_set_context(LOG_CTX_GB_NSVC, nsvc);
364
365 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK ACK");
366
367 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
368 nsvc->nse->nsei, nsvc->nsvci);
369
370 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
371}
372
Harald Welte5bef2cc2020-09-18 22:33:24 +0200373/*! Transmit a NS-ALIVE on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200374 * \param[in] nsvc NS-VC on which the NS-ALIVE is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200375 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200376int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
377{
Daniel Willmann751977b2020-12-02 18:59:44 +0100378 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200379 log_set_context(LOG_CTX_GB_NSVC, nsvc);
380 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n",
381 nsvc->nse->nsei, nsvc->nsvci);
382
383 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE);
384}
385
Harald Welte5bef2cc2020-09-18 22:33:24 +0200386/*! Transmit a NS-ALIVE-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200387 * \param[in] nsvc NS-VC on which the NS-ALIVE-ACK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200388 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200389int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc)
390{
Daniel Willmann751977b2020-12-02 18:59:44 +0100391 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200392 log_set_context(LOG_CTX_GB_NSVC, nsvc);
393 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n",
394 nsvc->nse->nsei, nsvc->nsvci);
395
396 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE_ACK);
397}
398
Harald Welte5bef2cc2020-09-18 22:33:24 +0200399/*! Transmit NS-UNITDATA on a given NS-VC.
400 * \param[in] nsvc NS-VC on which the NS-UNITDATA is to be transmitted
401 * \param[in] bvci BVCI to encode in NS-UNITDATA header
402 * \param[in] sducontrol SDU control octet of NS header
403 * \param[in] msg message buffer containing payload
404 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200405int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
406 uint16_t bvci, uint8_t sducontrol,
407 struct msgb *msg)
408{
409 struct gprs_ns_hdr *nsh;
410
Daniel Willmann751977b2020-12-02 18:59:44 +0100411 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200412 log_set_context(LOG_CTX_GB_NSVC, nsvc);
413
414 msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
415 nsh = (struct gprs_ns_hdr *) msg->l2h;
416 if (!nsh) {
417 LOGP(DLNS, LOGL_ERROR, "Not enough headroom for NS header\n");
418 msgb_free(msg);
419 return -EIO;
420 }
421
422 nsh->pdu_type = NS_PDUT_UNITDATA;
423 nsh->data[0] = sducontrol;
424 nsh->data[1] = bvci >> 8;
425 nsh->data[2] = bvci & 0xff;
426
427 return nsvc->bind->send_vc(nsvc, msg);
428}
429
Harald Welte5bef2cc2020-09-18 22:33:24 +0200430/*! Transmit a NS-STATUS on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200431 * \param[in] nsvc NS-VC to be used for transmission
432 * \param[in] cause Numeric NS cause value
433 * \param[in] bvci BVCI to be reset within NSVC
Harald Welte5bef2cc2020-09-18 22:33:24 +0200434 * \param[in] orig_msg message causing the STATUS
435 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200436int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
437 uint16_t bvci, struct msgb *orig_msg)
438{
439 struct msgb *msg = gprs_ns2_msgb_alloc();
440 struct gprs_ns_hdr *nsh;
441 uint16_t nsvci = osmo_htons(nsvc->nsvci);
442
Daniel Willmann751977b2020-12-02 18:59:44 +0100443 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200444 log_set_context(LOG_CTX_GB_NSVC, nsvc);
445
446 bvci = osmo_htons(bvci);
447
448 if (!msg)
449 return -ENOMEM;
450
451 LOGP(DLNS, LOGL_NOTICE, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n",
452 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
453
454 msg->l2h = msgb_put(msg, sizeof(*nsh));
455 nsh = (struct gprs_ns_hdr *) msg->l2h;
456 nsh->pdu_type = NS_PDUT_STATUS;
457
458 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
459
460 /* Section 9.2.7.1: Static conditions for NS-VCI */
461 if (cause == NS_CAUSE_NSVC_BLOCKED ||
462 cause == NS_CAUSE_NSVC_UNKNOWN)
463 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
464
465 /* Section 9.2.7.2: Static conditions for NS PDU */
466 switch (cause) {
467 case NS_CAUSE_SEM_INCORR_PDU:
468 case NS_CAUSE_PDU_INCOMP_PSTATE:
469 case NS_CAUSE_PROTO_ERR_UNSPEC:
470 case NS_CAUSE_INVAL_ESSENT_IE:
471 case NS_CAUSE_MISSING_ESSENT_IE:
472 msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg),
473 orig_msg->l2h);
474 break;
475 default:
476 break;
477 }
478
479 /* Section 9.2.7.3: Static conditions for BVCI */
480 if (cause == NS_CAUSE_BVCI_UNKNOWN)
481 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci);
482
483 return nsvc->bind->send_vc(nsvc, msg);
484}
485
486
487/*! Encode + Transmit a SNS-ACK as per Section 9.3.1.
488 * \param[in] nsvc NS-VC through which to transmit the ACK
489 * \param[in] trans_id Transaction ID which to acknowledge
490 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
491 * \param[in] ip4_elems Array of IPv4 Elements
492 * \param[in] num_ip4_elems number of ip4_elems
493 * \returns 0 on success; negative in case of error */
494int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
495 const struct gprs_ns_ie_ip4_elem *ip4_elems,
496 unsigned int num_ip4_elems,
497 const struct gprs_ns_ie_ip6_elem *ip6_elems,
498 unsigned int num_ip6_elems)
499{
Daniel Willmann89a00f32021-01-17 14:20:28 +0100500 struct msgb *msg;
Alexander Couzens6a161492020-07-12 13:45:50 +0200501 struct gprs_ns_hdr *nsh;
502 uint16_t nsei;
503
504 if (!nsvc)
505 return -1;
506
507 msg = gprs_ns2_msgb_alloc();
508
Daniel Willmann751977b2020-12-02 18:59:44 +0100509 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200510 log_set_context(LOG_CTX_GB_NSVC, nsvc);
511 if (!msg)
512 return -ENOMEM;
513
514 if (!nsvc->nse->bss_sns_fi) {
515 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
516 nsvc->nse->nsei);
517 msgb_free(msg);
518 return -EIO;
519 }
520
521 nsei = osmo_htons(nsvc->nse->nsei);
522
523 msg->l2h = msgb_put(msg, sizeof(*nsh));
524 nsh = (struct gprs_ns_hdr *) msg->l2h;
525
526 nsh->pdu_type = SNS_PDUT_ACK;
527 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
528 msgb_v_put(msg, trans_id);
529 if (cause)
530 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
531 if (ip4_elems) {
532 /* List of IP4 Elements 10.3.2c */
533 msgb_tvlv_put(msg, NS_IE_IPv4_LIST,
534 num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
535 (const uint8_t *)ip4_elems);
536 }
537 if (ip6_elems) {
538 /* List of IP6 elements 10.3.2d */
539 msgb_tvlv_put(msg, NS_IE_IPv6_LIST,
540 num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
541 (const uint8_t *)ip6_elems);
542 }
543
544 return nsvc->bind->send_vc(nsvc, msg);
545}
546
547/*! Encode + Transmit a SNS-CONFIG as per Section 9.3.4.
548 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
549 * \param[in] end_flag Whether or not this is the last SNS-CONFIG
550 * \param[in] ip4_elems Array of IPv4 Elements
551 * \param[in] num_ip4_elems number of ip4_elems
552 * \returns 0 on success; negative in case of error */
553int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
554 const struct gprs_ns_ie_ip4_elem *ip4_elems,
555 unsigned int num_ip4_elems,
556 const struct gprs_ns_ie_ip6_elem *ip6_elems,
557 unsigned int num_ip6_elems)
558{
559 struct msgb *msg;
560 struct gprs_ns_hdr *nsh;
561 uint16_t nsei;
562
563 if (!nsvc)
564 return -1;
565
566 msg = gprs_ns2_msgb_alloc();
567
Daniel Willmann751977b2020-12-02 18:59:44 +0100568 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200569 log_set_context(LOG_CTX_GB_NSVC, nsvc);
570 if (!msg)
571 return -ENOMEM;
572
573 if (!nsvc->nse->bss_sns_fi) {
574 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
575 nsvc->nse->nsei);
576 msgb_free(msg);
577 return -EIO;
578 }
579
580 nsei = osmo_htons(nsvc->nse->nsei);
581
582 msg->l2h = msgb_put(msg, sizeof(*nsh));
583 nsh = (struct gprs_ns_hdr *) msg->l2h;
584
585 nsh->pdu_type = SNS_PDUT_CONFIG;
586
587 msgb_v_put(msg, end_flag ? 0x01 : 0x00);
588 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
589
590 /* List of IP4 Elements 10.3.2c */
591 if (ip4_elems) {
592 msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
593 (const uint8_t *)ip4_elems);
594 } else if (ip6_elems) {
595 /* List of IP6 elements 10.3.2d */
596 msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
597 (const uint8_t *)ip6_elems);
598 }
599
600 return nsvc->bind->send_vc(nsvc, msg);
601}
602
603/*! Encode + Transmit a SNS-CONFIG-ACK as per Section 9.3.5.
604 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG-ACK
605 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
606 * \returns 0 on success; negative in case of error */
607int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
608{
609 struct msgb *msg;
610 struct gprs_ns_hdr *nsh;
611 uint16_t nsei;
612
613 if (!nsvc)
614 return -1;
615
616 msg = gprs_ns2_msgb_alloc();
Daniel Willmann751977b2020-12-02 18:59:44 +0100617 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200618 log_set_context(LOG_CTX_GB_NSVC, nsvc);
619 if (!msg)
620 return -ENOMEM;
621
622 if (!nsvc->nse->bss_sns_fi) {
623 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
624 nsvc->nse->nsei);
625 msgb_free(msg);
626 return -EIO;
627 }
628
629 nsei = osmo_htons(nsvc->nse->nsei);
630
631 msg->l2h = msgb_put(msg, sizeof(*nsh));
632 nsh = (struct gprs_ns_hdr *) msg->l2h;
633
634 nsh->pdu_type = SNS_PDUT_CONFIG_ACK;
635
636 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
637 if (cause)
638 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
639
640 return nsvc->bind->send_vc(nsvc, msg);
641}
642
643
644/*! Encode + transmit a SNS-SIZE as per Section 9.3.7.
645 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE
646 * \param[in] reset_flag Whether or not to add a RESET flag
647 * \param[in] max_nr_nsvc Maximum number of NS-VCs
648 * \param[in] ip4_ep_nr Number of IPv4 endpoints (< 0 will omit the TLV)
649 * \param[in] ip6_ep_nr Number of IPv6 endpoints (< 0 will omit the TLV)
650 * \returns 0 on success; negative in case of error */
651int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_nsvc,
652 int ip4_ep_nr, int ip6_ep_nr)
653{
Daniel Willmann89a00f32021-01-17 14:20:28 +0100654 struct msgb *msg;
Alexander Couzens6a161492020-07-12 13:45:50 +0200655 struct gprs_ns_hdr *nsh;
656 uint16_t nsei;
657
658 if (!nsvc)
659 return -1;
660
661 msg = gprs_ns2_msgb_alloc();
662
Daniel Willmann751977b2020-12-02 18:59:44 +0100663 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200664 log_set_context(LOG_CTX_GB_NSVC, nsvc);
665 if (!msg)
666 return -ENOMEM;
667
668 if (!nsvc->nse->bss_sns_fi) {
669 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
670 nsvc->nse->nsei);
671 msgb_free(msg);
672 return -EIO;
673 }
674
675 nsei = osmo_htons(nsvc->nse->nsei);
676
677 msg->l2h = msgb_put(msg, sizeof(*nsh));
678 nsh = (struct gprs_ns_hdr *) msg->l2h;
679
680 nsh->pdu_type = SNS_PDUT_SIZE;
681
682 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
683 msgb_tv_put(msg, NS_IE_RESET_FLAG, reset_flag ? 0x01 : 0x00);
684 msgb_tv16_put(msg, NS_IE_MAX_NR_NSVC, max_nr_nsvc);
685 if (ip4_ep_nr >= 0)
686 msgb_tv16_put(msg, NS_IE_IPv4_EP_NR, ip4_ep_nr);
687 if (ip6_ep_nr >= 0)
688 msgb_tv16_put(msg, NS_IE_IPv6_EP_NR, ip6_ep_nr);
689
690 return nsvc->bind->send_vc(nsvc, msg);
691}
692
693/*! Encode + Transmit a SNS-SIZE-ACK as per Section 9.3.8.
694 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE-ACK
695 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
696 * \returns 0 on success; negative in case of error */
697int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
698{
699 struct msgb *msg = gprs_ns2_msgb_alloc();
700 struct gprs_ns_hdr *nsh;
701 uint16_t nsei;
702
Daniel Willmann751977b2020-12-02 18:59:44 +0100703 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200704 log_set_context(LOG_CTX_GB_NSVC, nsvc);
705 if (!msg)
706 return -ENOMEM;
707
708 if (!nsvc->nse->bss_sns_fi) {
709 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
710 nsvc->nse->nsei);
711 msgb_free(msg);
712 return -EIO;
713 }
714
715 nsei = osmo_htons(nsvc->nse->nsei);
716
717 msg->l2h = msgb_put(msg, sizeof(*nsh));
718 nsh = (struct gprs_ns_hdr *) msg->l2h;
719
720 nsh->pdu_type = SNS_PDUT_SIZE_ACK;
721
722 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
723 if (cause)
724 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
725
726 return nsvc->bind->send_vc(nsvc, msg);
727}
728
729