blob: d97c2a567ba1e314e816b9d0935fc0228d086162 [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; \
Harald Weltef2949742021-01-20 14:54:14 +010046 LOGNSVC(nsvc, LOGL_DEBUG, "invalid packet %s with SNS\n", reason); \
Alexander Couzens6a161492020-07-12 13:45:50 +020047 } while (0)
48
Alexander Couzensf7e2cac2021-01-25 16:18:21 +010049static int 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 +020050{
Harald Welte798efea2020-12-03 16:01:02 +010051 if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1) ||
52 !TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
Alexander Couzens6a161492020-07-12 13:45:50 +020053 *cause = NS_CAUSE_MISSING_ESSENT_IE;
54 return -1;
55 }
56
57 return 0;
58}
59
Alexander Couzensf7e2cac2021-01-25 16:18:21 +010060static int 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 +020061{
Harald Welte798efea2020-12-03 16:01:02 +010062 if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
Alexander Couzens6a161492020-07-12 13:45:50 +020063 *cause = NS_CAUSE_MISSING_ESSENT_IE;
64 return -1;
65 }
66
67 return 0;
68}
69
Alexander Couzensf7e2cac2021-01-25 16:18:21 +010070static int 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 +020071{
Harald Welte798efea2020-12-03 16:01:02 +010072 if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
Alexander Couzens6a161492020-07-12 13:45:50 +020073 *cause = NS_CAUSE_MISSING_ESSENT_IE;
74 return -1;
75 }
76
77 return 0;
78}
79
Alexander Couzensf7e2cac2021-01-25 16:18:21 +010080static int 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 +020081{
Harald Welte798efea2020-12-03 16:01:02 +010082 if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2)) {
Alexander Couzens6a161492020-07-12 13:45:50 +020083 *cause = NS_CAUSE_MISSING_ESSENT_IE;
84 return -1;
85 }
86
87 return 0;
88}
89
Alexander Couzensf7e2cac2021-01-25 16:18:21 +010090static int 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 +020091{
92
Harald Welte798efea2020-12-03 16:01:02 +010093 if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
Alexander Couzens6a161492020-07-12 13:45:50 +020094 *cause = NS_CAUSE_MISSING_ESSENT_IE;
95 return -1;
96 }
97
98 uint8_t _cause = tlvp_val8(tp, NS_IE_VCI, 0);
99
100 switch (_cause) {
101 case NS_CAUSE_NSVC_BLOCKED:
102 case NS_CAUSE_NSVC_UNKNOWN:
Harald Welte798efea2020-12-03 16:01:02 +0100103 if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200104 *cause = NS_CAUSE_MISSING_ESSENT_IE;
105 return -1;
106 }
107 break;
108 case NS_CAUSE_SEM_INCORR_PDU:
109 case NS_CAUSE_PDU_INCOMP_PSTATE:
110 case NS_CAUSE_PROTO_ERR_UNSPEC:
111 case NS_CAUSE_INVAL_ESSENT_IE:
112 case NS_CAUSE_MISSING_ESSENT_IE:
Harald Welte798efea2020-12-03 16:01:02 +0100113 if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200114 *cause = NS_CAUSE_MISSING_ESSENT_IE;
115 return -1;
116 }
117 break;
118 case NS_CAUSE_BVCI_UNKNOWN:
Harald Welte798efea2020-12-03 16:01:02 +0100119 if (!TLVP_PRES_LEN(tp, NS_IE_BVCI, 2)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200120 *cause = NS_CAUSE_MISSING_ESSENT_IE;
121 return -1;
122 }
123 break;
124 case NS_CAUSE_UNKN_IP_TEST_FAILED:
Harald Welte798efea2020-12-03 16:01:02 +0100125 if (!TLVP_PRESENT(tp, NS_IE_IPv4_LIST) && !TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
Alexander Couzens6a161492020-07-12 13:45:50 +0200126 *cause = NS_CAUSE_MISSING_ESSENT_IE;
127 return -1;
128 }
129 break;
130 }
131
132 return 0;
133}
134
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100135int ns2_validate(struct gprs_ns2_vc *nsvc,
136 uint8_t pdu_type,
137 struct msgb *msg,
138 struct tlv_parsed *tp,
139 uint8_t *cause)
Alexander Couzens6a161492020-07-12 13:45:50 +0200140{
141 switch (pdu_type) {
142 case NS_PDUT_RESET:
Alexander Couzensf7e2cac2021-01-25 16:18:21 +0100143 return ns2_validate_reset(nsvc, msg, tp, cause);
Alexander Couzens6a161492020-07-12 13:45:50 +0200144 case NS_PDUT_RESET_ACK:
Alexander Couzensf7e2cac2021-01-25 16:18:21 +0100145 return ns2_validate_reset_ack(nsvc, msg, tp, cause);
Alexander Couzens6a161492020-07-12 13:45:50 +0200146 case NS_PDUT_BLOCK:
Alexander Couzensf7e2cac2021-01-25 16:18:21 +0100147 return ns2_validate_block(nsvc, msg, tp, cause);
Alexander Couzens6a161492020-07-12 13:45:50 +0200148 case NS_PDUT_BLOCK_ACK:
Alexander Couzensf7e2cac2021-01-25 16:18:21 +0100149 return ns2_validate_block_ack(nsvc, msg, tp, cause);
Alexander Couzens6a161492020-07-12 13:45:50 +0200150 case NS_PDUT_STATUS:
Alexander Couzensf7e2cac2021-01-25 16:18:21 +0100151 return ns2_validate_status(nsvc, msg, tp, cause);
Alexander Couzens6a161492020-07-12 13:45:50 +0200152
153 /* following PDUs doesn't have any payloads */
154 case NS_PDUT_ALIVE:
155 case NS_PDUT_ALIVE_ACK:
156 case NS_PDUT_UNBLOCK:
157 case NS_PDUT_UNBLOCK_ACK:
158 if (msgb_l2len(msg) != sizeof(struct gprs_ns_hdr)) {
159 *cause = NS_CAUSE_PROTO_ERR_UNSPEC;
160 return -1;
161 }
162 break;
163 }
164
165 return 0;
166}
167
168
Harald Welte5a5bf722021-01-30 21:49:28 +0100169static int ns_vc_tx(struct gprs_ns2_vc *nsvc, struct msgb *msg)
170{
171 return nsvc->bind->send_vc(nsvc, msg);
172}
173
Alexander Couzens6a161492020-07-12 13:45:50 +0200174/* transmit functions */
175static int ns2_tx_simple(struct gprs_ns2_vc *nsvc, uint8_t pdu_type)
176{
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100177 struct msgb *msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200178 struct gprs_ns_hdr *nsh;
179
Daniel Willmann751977b2020-12-02 18:59:44 +0100180 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200181 log_set_context(LOG_CTX_GB_NSVC, nsvc);
182
183 if (!msg)
184 return -ENOMEM;
185
186 msg->l2h = msgb_put(msg, sizeof(*nsh));
187 nsh = (struct gprs_ns_hdr *) msg->l2h;
188
189 nsh->pdu_type = pdu_type;
190
Harald Welte5a5bf722021-01-30 21:49:28 +0100191 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200192}
193
Harald Welte5bef2cc2020-09-18 22:33:24 +0200194/*! Transmit a NS-BLOCK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200195 * \param[in] vc NS-VC on which the NS-BLOCK is to be transmitted
196 * \param[in] cause Numeric NS Cause value
Harald Welte5bef2cc2020-09-18 22:33:24 +0200197 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200198int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause)
199{
200 struct msgb *msg;
201 struct gprs_ns_hdr *nsh;
202 uint16_t nsvci = osmo_htons(nsvc->nsvci);
203
Daniel Willmann751977b2020-12-02 18:59:44 +0100204 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200205 log_set_context(LOG_CTX_GB_NSVC, nsvc);
206
207 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK");
208
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100209 msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200210 if (!msg)
211 return -ENOMEM;
212
Harald Weltef2949742021-01-20 14:54:14 +0100213 LOGNSVC(nsvc, LOGL_INFO, "Tx NS BLOCK (cause=%s)\n", gprs_ns2_cause_str(cause));
Alexander Couzens6a161492020-07-12 13:45:50 +0200214
215 rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
216
217 msg->l2h = msgb_put(msg, sizeof(*nsh));
218 nsh = (struct gprs_ns_hdr *) msg->l2h;
219 nsh->pdu_type = NS_PDUT_BLOCK;
220
221 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
222 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
223
Harald Welte5a5bf722021-01-30 21:49:28 +0100224 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200225}
226
Harald Welte5bef2cc2020-09-18 22:33:24 +0200227/*! Transmit a NS-BLOCK-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200228 * \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200229 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200230int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc)
231{
232 struct msgb *msg;
233 struct gprs_ns_hdr *nsh;
234 uint16_t nsvci = osmo_htons(nsvc->nsvci);
235
Daniel Willmann751977b2020-12-02 18:59:44 +0100236 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200237 log_set_context(LOG_CTX_GB_NSVC, nsvc);
238
239 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK ACK");
240
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100241 msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200242 if (!msg)
243 return -ENOMEM;
244
Harald Weltef2949742021-01-20 14:54:14 +0100245 LOGNSVC(nsvc, LOGL_INFO, "Tx NS BLOCK ACK\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200246
Alexander Couzens6a161492020-07-12 13:45:50 +0200247 msg->l2h = msgb_put(msg, sizeof(*nsh));
248 nsh = (struct gprs_ns_hdr *) msg->l2h;
249 nsh->pdu_type = NS_PDUT_BLOCK_ACK;
250
251 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
252
Harald Welte5a5bf722021-01-30 21:49:28 +0100253 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200254}
255
Harald Welte5bef2cc2020-09-18 22:33:24 +0200256/*! Transmit a NS-RESET on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200257 * \param[in] nsvc NS-VC used for transmission
258 * \paam[in] cause Numeric NS cause value
Harald Welte5bef2cc2020-09-18 22:33:24 +0200259 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200260int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
261{
262 struct msgb *msg;
263 struct gprs_ns_hdr *nsh;
264 uint16_t nsvci = osmo_htons(nsvc->nsvci);
265 uint16_t nsei = osmo_htons(nsvc->nse->nsei);
266
Daniel Willmann751977b2020-12-02 18:59:44 +0100267 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200268 log_set_context(LOG_CTX_GB_NSVC, nsvc);
269
270 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
271
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100272 msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200273 if (!msg)
274 return -ENOMEM;
275
Harald Weltef2949742021-01-20 14:54:14 +0100276 LOGNSVC(nsvc, LOGL_INFO, "Tx NS RESET (cause=%s)\n", gprs_ns2_cause_str(cause));
Alexander Couzens6a161492020-07-12 13:45:50 +0200277
278 msg->l2h = msgb_put(msg, sizeof(*nsh));
279 nsh = (struct gprs_ns_hdr *) msg->l2h;
280 nsh->pdu_type = NS_PDUT_RESET;
281
282 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
283 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
284 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei);
285
Harald Welte5a5bf722021-01-30 21:49:28 +0100286 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200287}
288
Harald Welte5bef2cc2020-09-18 22:33:24 +0200289/*! Transmit a NS-RESET-ACK on a given NS-VC.
290 * \param[in] nsvc NS-VC used for transmission
291 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200292int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc)
293{
294 struct msgb *msg;
295 struct gprs_ns_hdr *nsh;
296 uint16_t nsvci, nsei;
297
Harald Welte5bef2cc2020-09-18 22:33:24 +0200298 /* Section 9.2.6 */
Daniel Willmann751977b2020-12-02 18:59:44 +0100299 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200300 log_set_context(LOG_CTX_GB_NSVC, nsvc);
301
302 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
303
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100304 msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200305 if (!msg)
306 return -ENOMEM;
307
308 nsvci = osmo_htons(nsvc->nsvci);
309 nsei = osmo_htons(nsvc->nse->nsei);
310
311 msg->l2h = msgb_put(msg, sizeof(*nsh));
312 nsh = (struct gprs_ns_hdr *) msg->l2h;
313
314 nsh->pdu_type = NS_PDUT_RESET_ACK;
315
Harald Weltef2949742021-01-20 14:54:14 +0100316 LOGNSVC(nsvc, LOGL_INFO, "Tx NS RESET ACK\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200317
318 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
319 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
320
Harald Welte5a5bf722021-01-30 21:49:28 +0100321 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200322}
323
Harald Welte5bef2cc2020-09-18 22:33:24 +0200324/*! Transmit a NS-UNBLOCK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200325 * \param[in] nsvc NS-VC on which the NS-UNBLOCK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200326 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200327int ns2_tx_unblock(struct gprs_ns2_vc *nsvc)
328{
Daniel Willmann751977b2020-12-02 18:59:44 +0100329 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200330 log_set_context(LOG_CTX_GB_NSVC, nsvc);
331
332 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK");
333
Harald Weltef2949742021-01-20 14:54:14 +0100334 LOGNSVC(nsvc, LOGL_INFO, "Tx NS UNBLOCK\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200335
336 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK);
337}
338
339
Harald Welte5bef2cc2020-09-18 22:33:24 +0200340/*! Transmit a NS-UNBLOCK-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200341 * \param[in] nsvc NS-VC on which the NS-UNBLOCK-ACK 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_ack(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 ACK");
349
Harald Weltef2949742021-01-20 14:54:14 +0100350 LOGNSVC(nsvc, LOGL_INFO, "Tx NS UNBLOCK_ACK\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200351
352 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
353}
354
Harald Welte5bef2cc2020-09-18 22:33:24 +0200355/*! Transmit a NS-ALIVE on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200356 * \param[in] nsvc NS-VC on which the NS-ALIVE is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200357 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200358int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
359{
Daniel Willmann751977b2020-12-02 18:59:44 +0100360 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200361 log_set_context(LOG_CTX_GB_NSVC, nsvc);
Harald Weltef2949742021-01-20 14:54:14 +0100362 LOGNSVC(nsvc, LOGL_DEBUG, "Tx NS ALIVE\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200363
364 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE);
365}
366
Harald Welte5bef2cc2020-09-18 22:33:24 +0200367/*! Transmit a NS-ALIVE-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200368 * \param[in] nsvc NS-VC on which the NS-ALIVE-ACK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200369 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200370int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc)
371{
Daniel Willmann751977b2020-12-02 18:59:44 +0100372 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200373 log_set_context(LOG_CTX_GB_NSVC, nsvc);
Harald Weltef2949742021-01-20 14:54:14 +0100374 LOGNSVC(nsvc, LOGL_DEBUG, "Tx NS ALIVE_ACK\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200375
376 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE_ACK);
377}
378
Harald Welte5bef2cc2020-09-18 22:33:24 +0200379/*! Transmit NS-UNITDATA on a given NS-VC.
380 * \param[in] nsvc NS-VC on which the NS-UNITDATA is to be transmitted
381 * \param[in] bvci BVCI to encode in NS-UNITDATA header
382 * \param[in] sducontrol SDU control octet of NS header
383 * \param[in] msg message buffer containing payload
384 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200385int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
386 uint16_t bvci, uint8_t sducontrol,
387 struct msgb *msg)
388{
389 struct gprs_ns_hdr *nsh;
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
394 msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
395 nsh = (struct gprs_ns_hdr *) msg->l2h;
396 if (!nsh) {
Harald Weltef2949742021-01-20 14:54:14 +0100397 LOGNSVC(nsvc, LOGL_ERROR, "Not enough headroom for NS header\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200398 msgb_free(msg);
399 return -EIO;
400 }
401
402 nsh->pdu_type = NS_PDUT_UNITDATA;
403 nsh->data[0] = sducontrol;
404 nsh->data[1] = bvci >> 8;
405 nsh->data[2] = bvci & 0xff;
406
Harald Welte5a5bf722021-01-30 21:49:28 +0100407 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200408}
409
Harald Welte5bef2cc2020-09-18 22:33:24 +0200410/*! Transmit a NS-STATUS on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200411 * \param[in] nsvc NS-VC to be used for transmission
412 * \param[in] cause Numeric NS cause value
413 * \param[in] bvci BVCI to be reset within NSVC
Harald Welte5bef2cc2020-09-18 22:33:24 +0200414 * \param[in] orig_msg message causing the STATUS
415 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200416int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
417 uint16_t bvci, struct msgb *orig_msg)
418{
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100419 struct msgb *msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200420 struct gprs_ns_hdr *nsh;
421 uint16_t nsvci = osmo_htons(nsvc->nsvci);
422
Daniel Willmann751977b2020-12-02 18:59:44 +0100423 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200424 log_set_context(LOG_CTX_GB_NSVC, nsvc);
425
426 bvci = osmo_htons(bvci);
427
428 if (!msg)
429 return -ENOMEM;
430
Harald Weltef2949742021-01-20 14:54:14 +0100431 LOGNSVC(nsvc, LOGL_NOTICE, "Tx NS STATUS (cause=%s)\n", gprs_ns2_cause_str(cause));
Alexander Couzens6a161492020-07-12 13:45:50 +0200432
433 msg->l2h = msgb_put(msg, sizeof(*nsh));
434 nsh = (struct gprs_ns_hdr *) msg->l2h;
435 nsh->pdu_type = NS_PDUT_STATUS;
436
437 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
438
439 /* Section 9.2.7.1: Static conditions for NS-VCI */
440 if (cause == NS_CAUSE_NSVC_BLOCKED ||
441 cause == NS_CAUSE_NSVC_UNKNOWN)
442 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
443
444 /* Section 9.2.7.2: Static conditions for NS PDU */
445 switch (cause) {
446 case NS_CAUSE_SEM_INCORR_PDU:
447 case NS_CAUSE_PDU_INCOMP_PSTATE:
448 case NS_CAUSE_PROTO_ERR_UNSPEC:
449 case NS_CAUSE_INVAL_ESSENT_IE:
450 case NS_CAUSE_MISSING_ESSENT_IE:
451 msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg),
452 orig_msg->l2h);
453 break;
454 default:
455 break;
456 }
457
458 /* Section 9.2.7.3: Static conditions for BVCI */
459 if (cause == NS_CAUSE_BVCI_UNKNOWN)
460 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci);
461
Harald Welte5a5bf722021-01-30 21:49:28 +0100462 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200463}
464
465
466/*! Encode + Transmit a SNS-ACK as per Section 9.3.1.
467 * \param[in] nsvc NS-VC through which to transmit the ACK
468 * \param[in] trans_id Transaction ID which to acknowledge
469 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
470 * \param[in] ip4_elems Array of IPv4 Elements
471 * \param[in] num_ip4_elems number of ip4_elems
472 * \returns 0 on success; negative in case of error */
473int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
474 const struct gprs_ns_ie_ip4_elem *ip4_elems,
475 unsigned int num_ip4_elems,
476 const struct gprs_ns_ie_ip6_elem *ip6_elems,
477 unsigned int num_ip6_elems)
478{
Daniel Willmann89a00f32021-01-17 14:20:28 +0100479 struct msgb *msg;
Alexander Couzens6a161492020-07-12 13:45:50 +0200480 struct gprs_ns_hdr *nsh;
481 uint16_t nsei;
482
483 if (!nsvc)
484 return -1;
485
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100486 msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200487
Daniel Willmann751977b2020-12-02 18:59:44 +0100488 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200489 log_set_context(LOG_CTX_GB_NSVC, nsvc);
490 if (!msg)
491 return -ENOMEM;
492
493 if (!nsvc->nse->bss_sns_fi) {
Harald Weltef2949742021-01-20 14:54:14 +0100494 LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200495 msgb_free(msg);
496 return -EIO;
497 }
498
499 nsei = osmo_htons(nsvc->nse->nsei);
500
501 msg->l2h = msgb_put(msg, sizeof(*nsh));
502 nsh = (struct gprs_ns_hdr *) msg->l2h;
503
504 nsh->pdu_type = SNS_PDUT_ACK;
505 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
506 msgb_v_put(msg, trans_id);
507 if (cause)
508 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
509 if (ip4_elems) {
510 /* List of IP4 Elements 10.3.2c */
511 msgb_tvlv_put(msg, NS_IE_IPv4_LIST,
512 num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
513 (const uint8_t *)ip4_elems);
514 }
515 if (ip6_elems) {
516 /* List of IP6 elements 10.3.2d */
517 msgb_tvlv_put(msg, NS_IE_IPv6_LIST,
518 num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
519 (const uint8_t *)ip6_elems);
520 }
521
Harald Welte5a5bf722021-01-30 21:49:28 +0100522 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200523}
524
525/*! Encode + Transmit a SNS-CONFIG as per Section 9.3.4.
526 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
527 * \param[in] end_flag Whether or not this is the last SNS-CONFIG
528 * \param[in] ip4_elems Array of IPv4 Elements
529 * \param[in] num_ip4_elems number of ip4_elems
530 * \returns 0 on success; negative in case of error */
531int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
532 const struct gprs_ns_ie_ip4_elem *ip4_elems,
533 unsigned int num_ip4_elems,
534 const struct gprs_ns_ie_ip6_elem *ip6_elems,
535 unsigned int num_ip6_elems)
536{
537 struct msgb *msg;
538 struct gprs_ns_hdr *nsh;
539 uint16_t nsei;
540
541 if (!nsvc)
542 return -1;
543
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100544 msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200545
Daniel Willmann751977b2020-12-02 18:59:44 +0100546 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200547 log_set_context(LOG_CTX_GB_NSVC, nsvc);
548 if (!msg)
549 return -ENOMEM;
550
551 if (!nsvc->nse->bss_sns_fi) {
Harald Weltef2949742021-01-20 14:54:14 +0100552 LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200553 msgb_free(msg);
554 return -EIO;
555 }
556
557 nsei = osmo_htons(nsvc->nse->nsei);
558
559 msg->l2h = msgb_put(msg, sizeof(*nsh));
560 nsh = (struct gprs_ns_hdr *) msg->l2h;
561
562 nsh->pdu_type = SNS_PDUT_CONFIG;
563
564 msgb_v_put(msg, end_flag ? 0x01 : 0x00);
565 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
566
567 /* List of IP4 Elements 10.3.2c */
568 if (ip4_elems) {
569 msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
570 (const uint8_t *)ip4_elems);
571 } else if (ip6_elems) {
572 /* List of IP6 elements 10.3.2d */
573 msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
574 (const uint8_t *)ip6_elems);
575 }
576
Harald Welte5a5bf722021-01-30 21:49:28 +0100577 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200578}
579
580/*! Encode + Transmit a SNS-CONFIG-ACK as per Section 9.3.5.
581 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG-ACK
582 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
583 * \returns 0 on success; negative in case of error */
584int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
585{
586 struct msgb *msg;
587 struct gprs_ns_hdr *nsh;
588 uint16_t nsei;
589
590 if (!nsvc)
591 return -1;
592
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100593 msg = ns2_msgb_alloc();
Daniel Willmann751977b2020-12-02 18:59:44 +0100594 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200595 log_set_context(LOG_CTX_GB_NSVC, nsvc);
596 if (!msg)
597 return -ENOMEM;
598
599 if (!nsvc->nse->bss_sns_fi) {
Harald Weltef2949742021-01-20 14:54:14 +0100600 LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200601 msgb_free(msg);
602 return -EIO;
603 }
604
605 nsei = osmo_htons(nsvc->nse->nsei);
606
607 msg->l2h = msgb_put(msg, sizeof(*nsh));
608 nsh = (struct gprs_ns_hdr *) msg->l2h;
609
610 nsh->pdu_type = SNS_PDUT_CONFIG_ACK;
611
612 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
613 if (cause)
614 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
615
Harald Welte5a5bf722021-01-30 21:49:28 +0100616 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200617}
618
619
620/*! Encode + transmit a SNS-SIZE as per Section 9.3.7.
621 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE
622 * \param[in] reset_flag Whether or not to add a RESET flag
623 * \param[in] max_nr_nsvc Maximum number of NS-VCs
624 * \param[in] ip4_ep_nr Number of IPv4 endpoints (< 0 will omit the TLV)
625 * \param[in] ip6_ep_nr Number of IPv6 endpoints (< 0 will omit the TLV)
626 * \returns 0 on success; negative in case of error */
627int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_nsvc,
628 int ip4_ep_nr, int ip6_ep_nr)
629{
Daniel Willmann89a00f32021-01-17 14:20:28 +0100630 struct msgb *msg;
Alexander Couzens6a161492020-07-12 13:45:50 +0200631 struct gprs_ns_hdr *nsh;
632 uint16_t nsei;
633
634 if (!nsvc)
635 return -1;
636
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100637 msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200638
Daniel Willmann751977b2020-12-02 18:59:44 +0100639 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200640 log_set_context(LOG_CTX_GB_NSVC, nsvc);
641 if (!msg)
642 return -ENOMEM;
643
644 if (!nsvc->nse->bss_sns_fi) {
Harald Weltef2949742021-01-20 14:54:14 +0100645 LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200646 msgb_free(msg);
647 return -EIO;
648 }
649
650 nsei = osmo_htons(nsvc->nse->nsei);
651
652 msg->l2h = msgb_put(msg, sizeof(*nsh));
653 nsh = (struct gprs_ns_hdr *) msg->l2h;
654
655 nsh->pdu_type = SNS_PDUT_SIZE;
656
657 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
658 msgb_tv_put(msg, NS_IE_RESET_FLAG, reset_flag ? 0x01 : 0x00);
659 msgb_tv16_put(msg, NS_IE_MAX_NR_NSVC, max_nr_nsvc);
660 if (ip4_ep_nr >= 0)
661 msgb_tv16_put(msg, NS_IE_IPv4_EP_NR, ip4_ep_nr);
662 if (ip6_ep_nr >= 0)
663 msgb_tv16_put(msg, NS_IE_IPv6_EP_NR, ip6_ep_nr);
664
Harald Welte5a5bf722021-01-30 21:49:28 +0100665 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200666}
667
668/*! Encode + Transmit a SNS-SIZE-ACK as per Section 9.3.8.
669 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE-ACK
670 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
671 * \returns 0 on success; negative in case of error */
672int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
673{
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100674 struct msgb *msg = ns2_msgb_alloc();
Alexander Couzens6a161492020-07-12 13:45:50 +0200675 struct gprs_ns_hdr *nsh;
676 uint16_t nsei;
677
Daniel Willmann751977b2020-12-02 18:59:44 +0100678 log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
Alexander Couzens6a161492020-07-12 13:45:50 +0200679 log_set_context(LOG_CTX_GB_NSVC, nsvc);
680 if (!msg)
681 return -ENOMEM;
682
683 if (!nsvc->nse->bss_sns_fi) {
Harald Weltef2949742021-01-20 14:54:14 +0100684 LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200685 msgb_free(msg);
686 return -EIO;
687 }
688
689 nsei = osmo_htons(nsvc->nse->nsei);
690
691 msg->l2h = msgb_put(msg, sizeof(*nsh));
692 nsh = (struct gprs_ns_hdr *) msg->l2h;
693
694 nsh->pdu_type = SNS_PDUT_SIZE_ACK;
695
696 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
697 if (cause)
698 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
699
Harald Welte5a5bf722021-01-30 21:49:28 +0100700 return ns_vc_tx(nsvc, msg);
Alexander Couzens6a161492020-07-12 13:45:50 +0200701}
702
703