blob: b4063f5e97905289858119aa7e3dd574220bc532 [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
67int gprs_ns2_validate_reset(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
68{
69 if (!TLVP_PRESENT(tp, NS_IE_CAUSE) || !TLVP_PRESENT(tp, NS_IE_VCI) || !TLVP_PRESENT(tp, NS_IE_NSEI)) {
70 *cause = NS_CAUSE_MISSING_ESSENT_IE;
71 return -1;
72 }
73
74 return 0;
75}
76
77int gprs_ns2_validate_reset_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
78{
79 if (!TLVP_PRESENT(tp, NS_IE_VCI) || !TLVP_PRESENT(tp, NS_IE_NSEI)) {
80 *cause = NS_CAUSE_MISSING_ESSENT_IE;
81 return -1;
82 }
83
84 return 0;
85}
86
87int gprs_ns2_validate_block(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
88{
89 if (!TLVP_PRESENT(tp, NS_IE_VCI) || !TLVP_PRESENT(tp, NS_IE_CAUSE)) {
90 *cause = NS_CAUSE_MISSING_ESSENT_IE;
91 return -1;
92 }
93
94 return 0;
95}
96
97int gprs_ns2_validate_block_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
98{
99 if (!TLVP_PRESENT(tp, NS_IE_VCI)) {
100 *cause = NS_CAUSE_MISSING_ESSENT_IE;
101 return -1;
102 }
103
104 return 0;
105}
106
107int gprs_ns2_validate_status(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
108{
109
110 if (!TLVP_PRESENT(tp, NS_IE_CAUSE)) {
111 *cause = NS_CAUSE_MISSING_ESSENT_IE;
112 return -1;
113 }
114
115 uint8_t _cause = tlvp_val8(tp, NS_IE_VCI, 0);
116
117 switch (_cause) {
118 case NS_CAUSE_NSVC_BLOCKED:
119 case NS_CAUSE_NSVC_UNKNOWN:
120 if (!TLVP_PRESENT(tp, NS_IE_CAUSE)) {
121 *cause = NS_CAUSE_MISSING_ESSENT_IE;
122 return -1;
123 }
124 break;
125 case NS_CAUSE_SEM_INCORR_PDU:
126 case NS_CAUSE_PDU_INCOMP_PSTATE:
127 case NS_CAUSE_PROTO_ERR_UNSPEC:
128 case NS_CAUSE_INVAL_ESSENT_IE:
129 case NS_CAUSE_MISSING_ESSENT_IE:
130 if (!TLVP_PRESENT(tp, NS_IE_CAUSE)) {
131 *cause = NS_CAUSE_MISSING_ESSENT_IE;
132 return -1;
133 }
134 break;
135 case NS_CAUSE_BVCI_UNKNOWN:
136 if (!TLVP_PRESENT(tp, NS_IE_BVCI)) {
137 *cause = NS_CAUSE_MISSING_ESSENT_IE;
138 return -1;
139 }
140 break;
141 case NS_CAUSE_UNKN_IP_TEST_FAILED:
142 if (!TLVP_PRESENT (tp, NS_IE_IPv4_LIST) && !TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
143 *cause = NS_CAUSE_MISSING_ESSENT_IE;
144 return -1;
145 }
146 break;
147 }
148
149 return 0;
150}
151
152int gprs_ns2_validate(struct gprs_ns2_vc *nsvc,
153 uint8_t pdu_type,
154 struct msgb *msg,
155 struct tlv_parsed *tp,
156 uint8_t *cause)
157{
158 switch (pdu_type) {
159 case NS_PDUT_RESET:
160 return gprs_ns2_validate_reset(nsvc, msg, tp, cause);
161 case NS_PDUT_RESET_ACK:
162 return gprs_ns2_validate_reset_ack(nsvc, msg, tp, cause);
163 case NS_PDUT_BLOCK:
164 return gprs_ns2_validate_block(nsvc, msg, tp, cause);
165 case NS_PDUT_BLOCK_ACK:
166 return gprs_ns2_validate_block_ack(nsvc, msg, tp, cause);
167 case NS_PDUT_STATUS:
168 return gprs_ns2_validate_status(nsvc, msg, tp, cause);
169
170 /* following PDUs doesn't have any payloads */
171 case NS_PDUT_ALIVE:
172 case NS_PDUT_ALIVE_ACK:
173 case NS_PDUT_UNBLOCK:
174 case NS_PDUT_UNBLOCK_ACK:
175 if (msgb_l2len(msg) != sizeof(struct gprs_ns_hdr)) {
176 *cause = NS_CAUSE_PROTO_ERR_UNSPEC;
177 return -1;
178 }
179 break;
180 }
181
182 return 0;
183}
184
185
186/* transmit functions */
187static int ns2_tx_simple(struct gprs_ns2_vc *nsvc, uint8_t pdu_type)
188{
189 struct msgb *msg = gprs_ns2_msgb_alloc();
190 struct gprs_ns_hdr *nsh;
191
192 log_set_context(LOG_CTX_GB_NSVC, nsvc);
193
194 if (!msg)
195 return -ENOMEM;
196
197 msg->l2h = msgb_put(msg, sizeof(*nsh));
198 nsh = (struct gprs_ns_hdr *) msg->l2h;
199
200 nsh->pdu_type = pdu_type;
201
202 return nsvc->bind->send_vc(nsvc, msg);
203}
204
Harald Welte5bef2cc2020-09-18 22:33:24 +0200205/*! Transmit a NS-BLOCK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200206 * \param[in] vc NS-VC on which the NS-BLOCK is to be transmitted
207 * \param[in] cause Numeric NS Cause value
Harald Welte5bef2cc2020-09-18 22:33:24 +0200208 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200209int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause)
210{
211 struct msgb *msg;
212 struct gprs_ns_hdr *nsh;
213 uint16_t nsvci = osmo_htons(nsvc->nsvci);
214
215 log_set_context(LOG_CTX_GB_NSVC, nsvc);
216
217 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK");
218
219 msg = gprs_ns2_msgb_alloc();
220 if (!msg)
221 return -ENOMEM;
222
223 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n",
224 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
225
226 rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
227
228 msg->l2h = msgb_put(msg, sizeof(*nsh));
229 nsh = (struct gprs_ns_hdr *) msg->l2h;
230 nsh->pdu_type = NS_PDUT_BLOCK;
231
232 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
233 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
234
235 return nsvc->bind->send_vc(nsvc, msg);
236}
237
Harald Welte5bef2cc2020-09-18 22:33:24 +0200238/*! Transmit a NS-BLOCK-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200239 * \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200240 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200241int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc)
242{
243 struct msgb *msg;
244 struct gprs_ns_hdr *nsh;
245 uint16_t nsvci = osmo_htons(nsvc->nsvci);
246
247 log_set_context(LOG_CTX_GB_NSVC, nsvc);
248
249 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK ACK");
250
251 msg = gprs_ns2_msgb_alloc();
252 if (!msg)
253 return -ENOMEM;
254
255 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK ACK (NSVCI=%u)\n", nsvc->nse->nsei, nsvc->nsvci);
256
257 /* be conservative and mark it as blocked even now! */
258 msg->l2h = msgb_put(msg, sizeof(*nsh));
259 nsh = (struct gprs_ns_hdr *) msg->l2h;
260 nsh->pdu_type = NS_PDUT_BLOCK_ACK;
261
262 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
263
264 return nsvc->bind->send_vc(nsvc, msg);
265}
266
Harald Welte5bef2cc2020-09-18 22:33:24 +0200267/*! Transmit a NS-RESET on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200268 * \param[in] nsvc NS-VC used for transmission
269 * \paam[in] cause Numeric NS cause value
Harald Welte5bef2cc2020-09-18 22:33:24 +0200270 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200271int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
272{
273 struct msgb *msg;
274 struct gprs_ns_hdr *nsh;
275 uint16_t nsvci = osmo_htons(nsvc->nsvci);
276 uint16_t nsei = osmo_htons(nsvc->nse->nsei);
277
278 log_set_context(LOG_CTX_GB_NSVC, nsvc);
279
280 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
281
282 msg = gprs_ns2_msgb_alloc();
283 if (!msg)
284 return -ENOMEM;
285
286 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n",
287 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
288
289 msg->l2h = msgb_put(msg, sizeof(*nsh));
290 nsh = (struct gprs_ns_hdr *) msg->l2h;
291 nsh->pdu_type = NS_PDUT_RESET;
292
293 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
294 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
295 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei);
296
297 return nsvc->bind->send_vc(nsvc, msg);
298}
299
Harald Welte5bef2cc2020-09-18 22:33:24 +0200300/*! Transmit a NS-RESET-ACK on a given NS-VC.
301 * \param[in] nsvc NS-VC used for transmission
302 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200303int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc)
304{
305 struct msgb *msg;
306 struct gprs_ns_hdr *nsh;
307 uint16_t nsvci, nsei;
308
Harald Welte5bef2cc2020-09-18 22:33:24 +0200309 /* Section 9.2.6 */
Alexander Couzens6a161492020-07-12 13:45:50 +0200310 log_set_context(LOG_CTX_GB_NSVC, nsvc);
311
312 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
313
314 msg = gprs_ns2_msgb_alloc();
315 if (!msg)
316 return -ENOMEM;
317
318 nsvci = osmo_htons(nsvc->nsvci);
319 nsei = osmo_htons(nsvc->nse->nsei);
320
321 msg->l2h = msgb_put(msg, sizeof(*nsh));
322 nsh = (struct gprs_ns_hdr *) msg->l2h;
323
324 nsh->pdu_type = NS_PDUT_RESET_ACK;
325
326 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n",
327 nsvc->nse->nsei, nsvc->nsvci);
328
329 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
330 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
331
332 return nsvc->bind->send_vc(nsvc, msg);
333}
334
Harald Welte5bef2cc2020-09-18 22:33:24 +0200335/*! Transmit a NS-UNBLOCK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200336 * \param[in] nsvc NS-VC on which the NS-UNBLOCK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200337 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200338int ns2_tx_unblock(struct gprs_ns2_vc *nsvc)
339{
340 log_set_context(LOG_CTX_GB_NSVC, nsvc);
341
342 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK");
343
344 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
345 nsvc->nse->nsei, nsvc->nsvci);
346
347 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK);
348}
349
350
Harald Welte5bef2cc2020-09-18 22:33:24 +0200351/*! Transmit a NS-UNBLOCK-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200352 * \param[in] nsvc NS-VC on which the NS-UNBLOCK-ACK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200353 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200354int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc)
355{
356 log_set_context(LOG_CTX_GB_NSVC, nsvc);
357
358 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK ACK");
359
360 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
361 nsvc->nse->nsei, nsvc->nsvci);
362
363 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
364}
365
Harald Welte5bef2cc2020-09-18 22:33:24 +0200366/*! Transmit a NS-ALIVE on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200367 * \param[in] nsvc NS-VC on which the NS-ALIVE is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200368 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200369int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
370{
371 log_set_context(LOG_CTX_GB_NSVC, nsvc);
372 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n",
373 nsvc->nse->nsei, nsvc->nsvci);
374
375 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE);
376}
377
Harald Welte5bef2cc2020-09-18 22:33:24 +0200378/*! Transmit a NS-ALIVE-ACK on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200379 * \param[in] nsvc NS-VC on which the NS-ALIVE-ACK is to be transmitted
Harald Welte5bef2cc2020-09-18 22:33:24 +0200380 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200381int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc)
382{
383 log_set_context(LOG_CTX_GB_NSVC, nsvc);
384 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n",
385 nsvc->nse->nsei, nsvc->nsvci);
386
387 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE_ACK);
388}
389
Harald Welte5bef2cc2020-09-18 22:33:24 +0200390/*! Transmit NS-UNITDATA on a given NS-VC.
391 * \param[in] nsvc NS-VC on which the NS-UNITDATA is to be transmitted
392 * \param[in] bvci BVCI to encode in NS-UNITDATA header
393 * \param[in] sducontrol SDU control octet of NS header
394 * \param[in] msg message buffer containing payload
395 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200396int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
397 uint16_t bvci, uint8_t sducontrol,
398 struct msgb *msg)
399{
400 struct gprs_ns_hdr *nsh;
401
402 log_set_context(LOG_CTX_GB_NSVC, nsvc);
403
404 msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
405 nsh = (struct gprs_ns_hdr *) msg->l2h;
406 if (!nsh) {
407 LOGP(DLNS, LOGL_ERROR, "Not enough headroom for NS header\n");
408 msgb_free(msg);
409 return -EIO;
410 }
411
412 nsh->pdu_type = NS_PDUT_UNITDATA;
413 nsh->data[0] = sducontrol;
414 nsh->data[1] = bvci >> 8;
415 nsh->data[2] = bvci & 0xff;
416
417 return nsvc->bind->send_vc(nsvc, msg);
418}
419
Harald Welte5bef2cc2020-09-18 22:33:24 +0200420/*! Transmit a NS-STATUS on a given NS-VC.
Alexander Couzens6a161492020-07-12 13:45:50 +0200421 * \param[in] nsvc NS-VC to be used for transmission
422 * \param[in] cause Numeric NS cause value
423 * \param[in] bvci BVCI to be reset within NSVC
Harald Welte5bef2cc2020-09-18 22:33:24 +0200424 * \param[in] orig_msg message causing the STATUS
425 * \returns 0 in case of success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200426int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
427 uint16_t bvci, struct msgb *orig_msg)
428{
429 struct msgb *msg = gprs_ns2_msgb_alloc();
430 struct gprs_ns_hdr *nsh;
431 uint16_t nsvci = osmo_htons(nsvc->nsvci);
432
433 log_set_context(LOG_CTX_GB_NSVC, nsvc);
434
435 bvci = osmo_htons(bvci);
436
437 if (!msg)
438 return -ENOMEM;
439
440 LOGP(DLNS, LOGL_NOTICE, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n",
441 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
442
443 msg->l2h = msgb_put(msg, sizeof(*nsh));
444 nsh = (struct gprs_ns_hdr *) msg->l2h;
445 nsh->pdu_type = NS_PDUT_STATUS;
446
447 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
448
449 /* Section 9.2.7.1: Static conditions for NS-VCI */
450 if (cause == NS_CAUSE_NSVC_BLOCKED ||
451 cause == NS_CAUSE_NSVC_UNKNOWN)
452 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
453
454 /* Section 9.2.7.2: Static conditions for NS PDU */
455 switch (cause) {
456 case NS_CAUSE_SEM_INCORR_PDU:
457 case NS_CAUSE_PDU_INCOMP_PSTATE:
458 case NS_CAUSE_PROTO_ERR_UNSPEC:
459 case NS_CAUSE_INVAL_ESSENT_IE:
460 case NS_CAUSE_MISSING_ESSENT_IE:
461 msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg),
462 orig_msg->l2h);
463 break;
464 default:
465 break;
466 }
467
468 /* Section 9.2.7.3: Static conditions for BVCI */
469 if (cause == NS_CAUSE_BVCI_UNKNOWN)
470 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci);
471
472 return nsvc->bind->send_vc(nsvc, msg);
473}
474
475
476/*! Encode + Transmit a SNS-ACK as per Section 9.3.1.
477 * \param[in] nsvc NS-VC through which to transmit the ACK
478 * \param[in] trans_id Transaction ID which to acknowledge
479 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
480 * \param[in] ip4_elems Array of IPv4 Elements
481 * \param[in] num_ip4_elems number of ip4_elems
482 * \returns 0 on success; negative in case of error */
483int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
484 const struct gprs_ns_ie_ip4_elem *ip4_elems,
485 unsigned int num_ip4_elems,
486 const struct gprs_ns_ie_ip6_elem *ip6_elems,
487 unsigned int num_ip6_elems)
488{
489 struct msgb *msg = gprs_ns2_msgb_alloc();
490 struct gprs_ns_hdr *nsh;
491 uint16_t nsei;
492
493 if (!nsvc)
494 return -1;
495
496 msg = gprs_ns2_msgb_alloc();
497
498 log_set_context(LOG_CTX_GB_NSVC, nsvc);
499 if (!msg)
500 return -ENOMEM;
501
502 if (!nsvc->nse->bss_sns_fi) {
503 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
504 nsvc->nse->nsei);
505 msgb_free(msg);
506 return -EIO;
507 }
508
509 nsei = osmo_htons(nsvc->nse->nsei);
510
511 msg->l2h = msgb_put(msg, sizeof(*nsh));
512 nsh = (struct gprs_ns_hdr *) msg->l2h;
513
514 nsh->pdu_type = SNS_PDUT_ACK;
515 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
516 msgb_v_put(msg, trans_id);
517 if (cause)
518 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
519 if (ip4_elems) {
520 /* List of IP4 Elements 10.3.2c */
521 msgb_tvlv_put(msg, NS_IE_IPv4_LIST,
522 num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
523 (const uint8_t *)ip4_elems);
524 }
525 if (ip6_elems) {
526 /* List of IP6 elements 10.3.2d */
527 msgb_tvlv_put(msg, NS_IE_IPv6_LIST,
528 num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
529 (const uint8_t *)ip6_elems);
530 }
531
532 return nsvc->bind->send_vc(nsvc, msg);
533}
534
535/*! Encode + Transmit a SNS-CONFIG as per Section 9.3.4.
536 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
537 * \param[in] end_flag Whether or not this is the last SNS-CONFIG
538 * \param[in] ip4_elems Array of IPv4 Elements
539 * \param[in] num_ip4_elems number of ip4_elems
540 * \returns 0 on success; negative in case of error */
541int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
542 const struct gprs_ns_ie_ip4_elem *ip4_elems,
543 unsigned int num_ip4_elems,
544 const struct gprs_ns_ie_ip6_elem *ip6_elems,
545 unsigned int num_ip6_elems)
546{
547 struct msgb *msg;
548 struct gprs_ns_hdr *nsh;
549 uint16_t nsei;
550
551 if (!nsvc)
552 return -1;
553
554 msg = gprs_ns2_msgb_alloc();
555
556 log_set_context(LOG_CTX_GB_NSVC, nsvc);
557 if (!msg)
558 return -ENOMEM;
559
560 if (!nsvc->nse->bss_sns_fi) {
561 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
562 nsvc->nse->nsei);
563 msgb_free(msg);
564 return -EIO;
565 }
566
567 nsei = osmo_htons(nsvc->nse->nsei);
568
569 msg->l2h = msgb_put(msg, sizeof(*nsh));
570 nsh = (struct gprs_ns_hdr *) msg->l2h;
571
572 nsh->pdu_type = SNS_PDUT_CONFIG;
573
574 msgb_v_put(msg, end_flag ? 0x01 : 0x00);
575 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
576
577 /* List of IP4 Elements 10.3.2c */
578 if (ip4_elems) {
579 msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
580 (const uint8_t *)ip4_elems);
581 } else if (ip6_elems) {
582 /* List of IP6 elements 10.3.2d */
583 msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
584 (const uint8_t *)ip6_elems);
585 }
586
587 return nsvc->bind->send_vc(nsvc, msg);
588}
589
590/*! Encode + Transmit a SNS-CONFIG-ACK as per Section 9.3.5.
591 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG-ACK
592 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
593 * \returns 0 on success; negative in case of error */
594int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
595{
596 struct msgb *msg;
597 struct gprs_ns_hdr *nsh;
598 uint16_t nsei;
599
600 if (!nsvc)
601 return -1;
602
603 msg = gprs_ns2_msgb_alloc();
604 log_set_context(LOG_CTX_GB_NSVC, nsvc);
605 if (!msg)
606 return -ENOMEM;
607
608 if (!nsvc->nse->bss_sns_fi) {
609 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
610 nsvc->nse->nsei);
611 msgb_free(msg);
612 return -EIO;
613 }
614
615 nsei = osmo_htons(nsvc->nse->nsei);
616
617 msg->l2h = msgb_put(msg, sizeof(*nsh));
618 nsh = (struct gprs_ns_hdr *) msg->l2h;
619
620 nsh->pdu_type = SNS_PDUT_CONFIG_ACK;
621
622 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
623 if (cause)
624 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
625
626 return nsvc->bind->send_vc(nsvc, msg);
627}
628
629
630/*! Encode + transmit a SNS-SIZE as per Section 9.3.7.
631 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE
632 * \param[in] reset_flag Whether or not to add a RESET flag
633 * \param[in] max_nr_nsvc Maximum number of NS-VCs
634 * \param[in] ip4_ep_nr Number of IPv4 endpoints (< 0 will omit the TLV)
635 * \param[in] ip6_ep_nr Number of IPv6 endpoints (< 0 will omit the TLV)
636 * \returns 0 on success; negative in case of error */
637int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_nsvc,
638 int ip4_ep_nr, int ip6_ep_nr)
639{
640 struct msgb *msg = gprs_ns2_msgb_alloc();
641 struct gprs_ns_hdr *nsh;
642 uint16_t nsei;
643
644 if (!nsvc)
645 return -1;
646
647 msg = gprs_ns2_msgb_alloc();
648
649 log_set_context(LOG_CTX_GB_NSVC, nsvc);
650 if (!msg)
651 return -ENOMEM;
652
653 if (!nsvc->nse->bss_sns_fi) {
654 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
655 nsvc->nse->nsei);
656 msgb_free(msg);
657 return -EIO;
658 }
659
660 nsei = osmo_htons(nsvc->nse->nsei);
661
662 msg->l2h = msgb_put(msg, sizeof(*nsh));
663 nsh = (struct gprs_ns_hdr *) msg->l2h;
664
665 nsh->pdu_type = SNS_PDUT_SIZE;
666
667 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
668 msgb_tv_put(msg, NS_IE_RESET_FLAG, reset_flag ? 0x01 : 0x00);
669 msgb_tv16_put(msg, NS_IE_MAX_NR_NSVC, max_nr_nsvc);
670 if (ip4_ep_nr >= 0)
671 msgb_tv16_put(msg, NS_IE_IPv4_EP_NR, ip4_ep_nr);
672 if (ip6_ep_nr >= 0)
673 msgb_tv16_put(msg, NS_IE_IPv6_EP_NR, ip6_ep_nr);
674
675 return nsvc->bind->send_vc(nsvc, msg);
676}
677
678/*! Encode + Transmit a SNS-SIZE-ACK as per Section 9.3.8.
679 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE-ACK
680 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
681 * \returns 0 on success; negative in case of error */
682int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
683{
684 struct msgb *msg = gprs_ns2_msgb_alloc();
685 struct gprs_ns_hdr *nsh;
686 uint16_t nsei;
687
688 log_set_context(LOG_CTX_GB_NSVC, nsvc);
689 if (!msg)
690 return -ENOMEM;
691
692 if (!nsvc->nse->bss_sns_fi) {
693 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
694 nsvc->nse->nsei);
695 msgb_free(msg);
696 return -EIO;
697 }
698
699 nsei = osmo_htons(nsvc->nse->nsei);
700
701 msg->l2h = msgb_put(msg, sizeof(*nsh));
702 nsh = (struct gprs_ns_hdr *) msg->l2h;
703
704 nsh->pdu_type = SNS_PDUT_SIZE_ACK;
705
706 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
707 if (cause)
708 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
709
710 return nsvc->bind->send_vc(nsvc, msg);
711}
712
713