blob: 6c7f08fd36a4f0dcb4f12345d03edaf45966e1a8 [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
261 /* be conservative and mark it as blocked even now! */
262 msg->l2h = msgb_put(msg, sizeof(*nsh));
263 nsh = (struct gprs_ns_hdr *) msg->l2h;
264 nsh->pdu_type = NS_PDUT_BLOCK_ACK;
265
266 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
267
268 return nsvc->bind->send_vc(nsvc, msg);
269}
270
Harald Welte5bef2cc2020-09-18 22:33:24 +0200271/*! Transmit a NS-RESET on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200272 * \param[in] nsvc NS-VC used for transmission
273 * \paam[in] cause Numeric NS cause value
Harald Welte5bef2cc2020-09-18 22:33:24 +0200274 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200275int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
276{
277 struct msgb *msg;
278 struct gprs_ns_hdr *nsh;
279 uint16_t nsvci = osmo_htons(nsvc->nsvci);
280 uint16_t nsei = osmo_htons(nsvc->nse->nsei);
281
Daniel Willmann751977b2020-12-02 18:59:44 +0100282 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200283 log_set_context(LOG_CTX_GB_NSVC, nsvc);
284
285 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
286
287 msg = gprs_ns2_msgb_alloc();
288 if (!msg)
289 return -ENOMEM;
290
291 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n",
292 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
293
294 msg->l2h = msgb_put(msg, sizeof(*nsh));
295 nsh = (struct gprs_ns_hdr *) msg->l2h;
296 nsh->pdu_type = NS_PDUT_RESET;
297
298 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
299 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
300 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei);
301
302 return nsvc->bind->send_vc(nsvc, msg);
303}
304
Harald Welte5bef2cc2020-09-18 22:33:24 +0200305/*! Transmit a NS-RESET-ACK on a given NS-VC.
306 * \param[in] nsvc NS-VC used for transmission
307 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200308int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc)
309{
310 struct msgb *msg;
311 struct gprs_ns_hdr *nsh;
312 uint16_t nsvci, nsei;
313
Harald Welte5bef2cc2020-09-18 22:33:24 +0200314 /* Section 9.2.6 */
Daniel Willmann751977b2020-12-02 18:59:44 +0100315 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200316 log_set_context(LOG_CTX_GB_NSVC, nsvc);
317
318 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
319
320 msg = gprs_ns2_msgb_alloc();
321 if (!msg)
322 return -ENOMEM;
323
324 nsvci = osmo_htons(nsvc->nsvci);
325 nsei = osmo_htons(nsvc->nse->nsei);
326
327 msg->l2h = msgb_put(msg, sizeof(*nsh));
328 nsh = (struct gprs_ns_hdr *) msg->l2h;
329
330 nsh->pdu_type = NS_PDUT_RESET_ACK;
331
332 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n",
333 nsvc->nse->nsei, nsvc->nsvci);
334
335 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
336 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
337
338 return nsvc->bind->send_vc(nsvc, msg);
339}
340
Harald Welte5bef2cc2020-09-18 22:33:24 +0200341/*! Transmit a NS-UNBLOCK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200342 * \param[in] nsvc NS-VC on which the NS-UNBLOCK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200343 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200344int ns2_tx_unblock(struct gprs_ns2_vc *nsvc)
345{
Daniel Willmann751977b2020-12-02 18:59:44 +0100346 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200347 log_set_context(LOG_CTX_GB_NSVC, nsvc);
348
349 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK");
350
351 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
352 nsvc->nse->nsei, nsvc->nsvci);
353
354 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK);
355}
356
357
Harald Welte5bef2cc2020-09-18 22:33:24 +0200358/*! Transmit a NS-UNBLOCK-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200359 * \param[in] nsvc NS-VC on which the NS-UNBLOCK-ACK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200360 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200361int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc)
362{
Daniel Willmann751977b2020-12-02 18:59:44 +0100363 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200364 log_set_context(LOG_CTX_GB_NSVC, nsvc);
365
366 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK ACK");
367
368 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
369 nsvc->nse->nsei, nsvc->nsvci);
370
371 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
372}
373
Harald Welte5bef2cc2020-09-18 22:33:24 +0200374/*! Transmit a NS-ALIVE on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200375 * \param[in] nsvc NS-VC on which the NS-ALIVE is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200376 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200377int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
378{
Daniel Willmann751977b2020-12-02 18:59:44 +0100379 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200380 log_set_context(LOG_CTX_GB_NSVC, nsvc);
381 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n",
382 nsvc->nse->nsei, nsvc->nsvci);
383
384 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE);
385}
386
Harald Welte5bef2cc2020-09-18 22:33:24 +0200387/*! Transmit a NS-ALIVE-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200388 * \param[in] nsvc NS-VC on which the NS-ALIVE-ACK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200389 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200390int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc)
391{
Daniel Willmann751977b2020-12-02 18:59:44 +0100392 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200393 log_set_context(LOG_CTX_GB_NSVC, nsvc);
394 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n",
395 nsvc->nse->nsei, nsvc->nsvci);
396
397 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE_ACK);
398}
399
Harald Welte5bef2cc2020-09-18 22:33:24 +0200400/*! Transmit NS-UNITDATA on a given NS-VC.
401 * \param[in] nsvc NS-VC on which the NS-UNITDATA is to be transmitted
402 * \param[in] bvci BVCI to encode in NS-UNITDATA header
403 * \param[in] sducontrol SDU control octet of NS header
404 * \param[in] msg message buffer containing payload
405 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200406int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
407 uint16_t bvci, uint8_t sducontrol,
408 struct msgb *msg)
409{
410 struct gprs_ns_hdr *nsh;
411
Daniel Willmann751977b2020-12-02 18:59:44 +0100412 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200413 log_set_context(LOG_CTX_GB_NSVC, nsvc);
414
415 msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
416 nsh = (struct gprs_ns_hdr *) msg->l2h;
417 if (!nsh) {
418 LOGP(DLNS, LOGL_ERROR, "Not enough headroom for NS header\n");
419 msgb_free(msg);
420 return -EIO;
421 }
422
423 nsh->pdu_type = NS_PDUT_UNITDATA;
424 nsh->data[0] = sducontrol;
425 nsh->data[1] = bvci >> 8;
426 nsh->data[2] = bvci & 0xff;
427
428 return nsvc->bind->send_vc(nsvc, msg);
429}
430
Harald Welte5bef2cc2020-09-18 22:33:24 +0200431/*! Transmit a NS-STATUS on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200432 * \param[in] nsvc NS-VC to be used for transmission
433 * \param[in] cause Numeric NS cause value
434 * \param[in] bvci BVCI to be reset within NSVC
Harald Welte5bef2cc2020-09-18 22:33:24 +0200435 * \param[in] orig_msg message causing the STATUS
436 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200437int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
438 uint16_t bvci, struct msgb *orig_msg)
439{
440 struct msgb *msg = gprs_ns2_msgb_alloc();
441 struct gprs_ns_hdr *nsh;
442 uint16_t nsvci = osmo_htons(nsvc->nsvci);
443
Daniel Willmann751977b2020-12-02 18:59:44 +0100444 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200445 log_set_context(LOG_CTX_GB_NSVC, nsvc);
446
447 bvci = osmo_htons(bvci);
448
449 if (!msg)
450 return -ENOMEM;
451
452 LOGP(DLNS, LOGL_NOTICE, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n",
453 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
454
455 msg->l2h = msgb_put(msg, sizeof(*nsh));
456 nsh = (struct gprs_ns_hdr *) msg->l2h;
457 nsh->pdu_type = NS_PDUT_STATUS;
458
459 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
460
461 /* Section 9.2.7.1: Static conditions for NS-VCI */
462 if (cause == NS_CAUSE_NSVC_BLOCKED ||
463 cause == NS_CAUSE_NSVC_UNKNOWN)
464 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
465
466 /* Section 9.2.7.2: Static conditions for NS PDU */
467 switch (cause) {
468 case NS_CAUSE_SEM_INCORR_PDU:
469 case NS_CAUSE_PDU_INCOMP_PSTATE:
470 case NS_CAUSE_PROTO_ERR_UNSPEC:
471 case NS_CAUSE_INVAL_ESSENT_IE:
472 case NS_CAUSE_MISSING_ESSENT_IE:
473 msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg),
474 orig_msg->l2h);
475 break;
476 default:
477 break;
478 }
479
480 /* Section 9.2.7.3: Static conditions for BVCI */
481 if (cause == NS_CAUSE_BVCI_UNKNOWN)
482 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci);
483
484 return nsvc->bind->send_vc(nsvc, msg);
485}
486
487
488/*! Encode + Transmit a SNS-ACK as per Section 9.3.1.
489 * \param[in] nsvc NS-VC through which to transmit the ACK
490 * \param[in] trans_id Transaction ID which to acknowledge
491 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
492 * \param[in] ip4_elems Array of IPv4 Elements
493 * \param[in] num_ip4_elems number of ip4_elems
494 * \returns 0 on success; negative in case of error */
495int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
496 const struct gprs_ns_ie_ip4_elem *ip4_elems,
497 unsigned int num_ip4_elems,
498 const struct gprs_ns_ie_ip6_elem *ip6_elems,
499 unsigned int num_ip6_elems)
500{
Daniel Willmann89a00f32021-01-17 14:20:28 +0100501 struct msgb *msg;
Alexander Couzens6a161492020-07-12 13:45:50 +0200502 struct gprs_ns_hdr *nsh;
503 uint16_t nsei;
504
505 if (!nsvc)
506 return -1;
507
508 msg = gprs_ns2_msgb_alloc();
509
Daniel Willmann751977b2020-12-02 18:59:44 +0100510 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200511 log_set_context(LOG_CTX_GB_NSVC, nsvc);
512 if (!msg)
513 return -ENOMEM;
514
515 if (!nsvc->nse->bss_sns_fi) {
516 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
517 nsvc->nse->nsei);
518 msgb_free(msg);
519 return -EIO;
520 }
521
522 nsei = osmo_htons(nsvc->nse->nsei);
523
524 msg->l2h = msgb_put(msg, sizeof(*nsh));
525 nsh = (struct gprs_ns_hdr *) msg->l2h;
526
527 nsh->pdu_type = SNS_PDUT_ACK;
528 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
529 msgb_v_put(msg, trans_id);
530 if (cause)
531 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
532 if (ip4_elems) {
533 /* List of IP4 Elements 10.3.2c */
534 msgb_tvlv_put(msg, NS_IE_IPv4_LIST,
535 num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
536 (const uint8_t *)ip4_elems);
537 }
538 if (ip6_elems) {
539 /* List of IP6 elements 10.3.2d */
540 msgb_tvlv_put(msg, NS_IE_IPv6_LIST,
541 num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
542 (const uint8_t *)ip6_elems);
543 }
544
545 return nsvc->bind->send_vc(nsvc, msg);
546}
547
548/*! Encode + Transmit a SNS-CONFIG as per Section 9.3.4.
549 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
550 * \param[in] end_flag Whether or not this is the last SNS-CONFIG
551 * \param[in] ip4_elems Array of IPv4 Elements
552 * \param[in] num_ip4_elems number of ip4_elems
553 * \returns 0 on success; negative in case of error */
554int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
555 const struct gprs_ns_ie_ip4_elem *ip4_elems,
556 unsigned int num_ip4_elems,
557 const struct gprs_ns_ie_ip6_elem *ip6_elems,
558 unsigned int num_ip6_elems)
559{
560 struct msgb *msg;
561 struct gprs_ns_hdr *nsh;
562 uint16_t nsei;
563
564 if (!nsvc)
565 return -1;
566
567 msg = gprs_ns2_msgb_alloc();
568
Daniel Willmann751977b2020-12-02 18:59:44 +0100569 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200570 log_set_context(LOG_CTX_GB_NSVC, nsvc);
571 if (!msg)
572 return -ENOMEM;
573
574 if (!nsvc->nse->bss_sns_fi) {
575 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
576 nsvc->nse->nsei);
577 msgb_free(msg);
578 return -EIO;
579 }
580
581 nsei = osmo_htons(nsvc->nse->nsei);
582
583 msg->l2h = msgb_put(msg, sizeof(*nsh));
584 nsh = (struct gprs_ns_hdr *) msg->l2h;
585
586 nsh->pdu_type = SNS_PDUT_CONFIG;
587
588 msgb_v_put(msg, end_flag ? 0x01 : 0x00);
589 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
590
591 /* List of IP4 Elements 10.3.2c */
592 if (ip4_elems) {
593 msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
594 (const uint8_t *)ip4_elems);
595 } else if (ip6_elems) {
596 /* List of IP6 elements 10.3.2d */
597 msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
598 (const uint8_t *)ip6_elems);
599 }
600
601 return nsvc->bind->send_vc(nsvc, msg);
602}
603
604/*! Encode + Transmit a SNS-CONFIG-ACK as per Section 9.3.5.
605 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG-ACK
606 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
607 * \returns 0 on success; negative in case of error */
608int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
609{
610 struct msgb *msg;
611 struct gprs_ns_hdr *nsh;
612 uint16_t nsei;
613
614 if (!nsvc)
615 return -1;
616
617 msg = gprs_ns2_msgb_alloc();
Daniel Willmann751977b2020-12-02 18:59:44 +0100618 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200619 log_set_context(LOG_CTX_GB_NSVC, nsvc);
620 if (!msg)
621 return -ENOMEM;
622
623 if (!nsvc->nse->bss_sns_fi) {
624 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
625 nsvc->nse->nsei);
626 msgb_free(msg);
627 return -EIO;
628 }
629
630 nsei = osmo_htons(nsvc->nse->nsei);
631
632 msg->l2h = msgb_put(msg, sizeof(*nsh));
633 nsh = (struct gprs_ns_hdr *) msg->l2h;
634
635 nsh->pdu_type = SNS_PDUT_CONFIG_ACK;
636
637 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
638 if (cause)
639 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
640
641 return nsvc->bind->send_vc(nsvc, msg);
642}
643
644
645/*! Encode + transmit a SNS-SIZE as per Section 9.3.7.
646 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE
647 * \param[in] reset_flag Whether or not to add a RESET flag
648 * \param[in] max_nr_nsvc Maximum number of NS-VCs
649 * \param[in] ip4_ep_nr Number of IPv4 endpoints (< 0 will omit the TLV)
650 * \param[in] ip6_ep_nr Number of IPv6 endpoints (< 0 will omit the TLV)
651 * \returns 0 on success; negative in case of error */
652int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_nsvc,
653 int ip4_ep_nr, int ip6_ep_nr)
654{
Daniel Willmann89a00f32021-01-17 14:20:28 +0100655 struct msgb *msg;
Alexander Couzens6a161492020-07-12 13:45:50 +0200656 struct gprs_ns_hdr *nsh;
657 uint16_t nsei;
658
659 if (!nsvc)
660 return -1;
661
662 msg = gprs_ns2_msgb_alloc();
663
Daniel Willmann751977b2020-12-02 18:59:44 +0100664 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200665 log_set_context(LOG_CTX_GB_NSVC, nsvc);
666 if (!msg)
667 return -ENOMEM;
668
669 if (!nsvc->nse->bss_sns_fi) {
670 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
671 nsvc->nse->nsei);
672 msgb_free(msg);
673 return -EIO;
674 }
675
676 nsei = osmo_htons(nsvc->nse->nsei);
677
678 msg->l2h = msgb_put(msg, sizeof(*nsh));
679 nsh = (struct gprs_ns_hdr *) msg->l2h;
680
681 nsh->pdu_type = SNS_PDUT_SIZE;
682
683 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
684 msgb_tv_put(msg, NS_IE_RESET_FLAG, reset_flag ? 0x01 : 0x00);
685 msgb_tv16_put(msg, NS_IE_MAX_NR_NSVC, max_nr_nsvc);
686 if (ip4_ep_nr >= 0)
687 msgb_tv16_put(msg, NS_IE_IPv4_EP_NR, ip4_ep_nr);
688 if (ip6_ep_nr >= 0)
689 msgb_tv16_put(msg, NS_IE_IPv6_EP_NR, ip6_ep_nr);
690
691 return nsvc->bind->send_vc(nsvc, msg);
692}
693
694/*! Encode + Transmit a SNS-SIZE-ACK as per Section 9.3.8.
695 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE-ACK
696 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
697 * \returns 0 on success; negative in case of error */
698int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
699{
700 struct msgb *msg = gprs_ns2_msgb_alloc();
701 struct gprs_ns_hdr *nsh;
702 uint16_t nsei;
703
Daniel Willmann751977b2020-12-02 18:59:44 +0100704 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200705 log_set_context(LOG_CTX_GB_NSVC, nsvc);
706 if (!msg)
707 return -ENOMEM;
708
709 if (!nsvc->nse->bss_sns_fi) {
710 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
711 nsvc->nse->nsei);
712 msgb_free(msg);
713 return -EIO;
714 }
715
716 nsei = osmo_htons(nsvc->nse->nsei);
717
718 msg->l2h = msgb_put(msg, sizeof(*nsh));
719 nsh = (struct gprs_ns_hdr *) msg->l2h;
720
721 nsh->pdu_type = SNS_PDUT_SIZE_ACK;
722
723 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
724 if (cause)
725 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
726
727 return nsvc->bind->send_vc(nsvc, msg);
728}
729
730