blob: 7742404ef3ba4c447375590e813f454ac489b1e7 [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
205/*! Transmit a NS-BLOCK on a given NS-VC
206 * \param[in] vc NS-VC on which the NS-BLOCK is to be transmitted
207 * \param[in] cause Numeric NS Cause value
208 * \returns 0 in case of success
209 */
210int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause)
211{
212 struct msgb *msg;
213 struct gprs_ns_hdr *nsh;
214 uint16_t nsvci = osmo_htons(nsvc->nsvci);
215
216 log_set_context(LOG_CTX_GB_NSVC, nsvc);
217
218 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK");
219
220 msg = gprs_ns2_msgb_alloc();
221 if (!msg)
222 return -ENOMEM;
223
224 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n",
225 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
226
227 rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
228
229 msg->l2h = msgb_put(msg, sizeof(*nsh));
230 nsh = (struct gprs_ns_hdr *) msg->l2h;
231 nsh->pdu_type = NS_PDUT_BLOCK;
232
233 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
234 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
235
236 return nsvc->bind->send_vc(nsvc, msg);
237}
238
239/*! Transmit a NS-BLOCK-ACK on a given NS-VC
240 * \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted
241 * \returns 0 in case of success
242 */
243int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc)
244{
245 struct msgb *msg;
246 struct gprs_ns_hdr *nsh;
247 uint16_t nsvci = osmo_htons(nsvc->nsvci);
248
249 log_set_context(LOG_CTX_GB_NSVC, nsvc);
250
251 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK ACK");
252
253 msg = gprs_ns2_msgb_alloc();
254 if (!msg)
255 return -ENOMEM;
256
257 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK ACK (NSVCI=%u)\n", nsvc->nse->nsei, nsvc->nsvci);
258
259 /* be conservative and mark it as blocked even now! */
260 msg->l2h = msgb_put(msg, sizeof(*nsh));
261 nsh = (struct gprs_ns_hdr *) msg->l2h;
262 nsh->pdu_type = NS_PDUT_BLOCK_ACK;
263
264 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
265
266 return nsvc->bind->send_vc(nsvc, msg);
267}
268
269/*! Transmit a NS-RESET on a given NSVC
270 * \param[in] nsvc NS-VC used for transmission
271 * \paam[in] cause Numeric NS cause value
272 */
273int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
274{
275 struct msgb *msg;
276 struct gprs_ns_hdr *nsh;
277 uint16_t nsvci = osmo_htons(nsvc->nsvci);
278 uint16_t nsei = osmo_htons(nsvc->nse->nsei);
279
280 log_set_context(LOG_CTX_GB_NSVC, nsvc);
281
282 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
283
284 msg = gprs_ns2_msgb_alloc();
285 if (!msg)
286 return -ENOMEM;
287
288 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n",
289 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
290
291 msg->l2h = msgb_put(msg, sizeof(*nsh));
292 nsh = (struct gprs_ns_hdr *) msg->l2h;
293 nsh->pdu_type = NS_PDUT_RESET;
294
295 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
296 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
297 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei);
298
299 return nsvc->bind->send_vc(nsvc, msg);
300}
301
302/* Section 9.2.6 */
303int 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
309 log_set_context(LOG_CTX_GB_NSVC, nsvc);
310
311 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
312
313 msg = gprs_ns2_msgb_alloc();
314 if (!msg)
315 return -ENOMEM;
316
317 nsvci = osmo_htons(nsvc->nsvci);
318 nsei = osmo_htons(nsvc->nse->nsei);
319
320 msg->l2h = msgb_put(msg, sizeof(*nsh));
321 nsh = (struct gprs_ns_hdr *) msg->l2h;
322
323 nsh->pdu_type = NS_PDUT_RESET_ACK;
324
325 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n",
326 nsvc->nse->nsei, nsvc->nsvci);
327
328 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
329 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
330
331 return nsvc->bind->send_vc(nsvc, msg);
332}
333
334/*! Transmit a NS-UNBLOCK on a given NS-VC
335 * \param[in] nsvc NS-VC on which the NS-UNBLOCK is to be transmitted
336 * \returns 0 in case of success
337 */
338int 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
351/*! Transmit a NS-UNBLOCK-ACK on a given NS-VC
352 * \param[in] nsvc NS-VC on which the NS-UNBLOCK-ACK is to be transmitted
353 * \returns 0 in case of success
354 */
355int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc)
356{
357 log_set_context(LOG_CTX_GB_NSVC, nsvc);
358
359 ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK ACK");
360
361 LOGP(DLNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
362 nsvc->nse->nsei, nsvc->nsvci);
363
364 return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
365}
366
367/*! Transmit a NS-ALIVE on a given NS-VC
368 * \param[in] nsvc NS-VC on which the NS-ALIVE is to be transmitted
369 * \returns 0 in case of success
370 */
371int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
372{
373 log_set_context(LOG_CTX_GB_NSVC, nsvc);
374 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n",
375 nsvc->nse->nsei, nsvc->nsvci);
376
377 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE);
378}
379
380/*! Transmit a NS-ALIVE-ACK on a given NS-VC
381 * \param[in] nsvc NS-VC on which the NS-ALIVE-ACK is to be transmitted
382 * \returns 0 in case of success
383 */
384int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc)
385{
386 log_set_context(LOG_CTX_GB_NSVC, nsvc);
387 LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n",
388 nsvc->nse->nsei, nsvc->nsvci);
389
390 return ns2_tx_simple(nsvc, NS_PDUT_ALIVE_ACK);
391}
392
393int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
394 uint16_t bvci, uint8_t sducontrol,
395 struct msgb *msg)
396{
397 struct gprs_ns_hdr *nsh;
398
399 log_set_context(LOG_CTX_GB_NSVC, nsvc);
400
401 msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
402 nsh = (struct gprs_ns_hdr *) msg->l2h;
403 if (!nsh) {
404 LOGP(DLNS, LOGL_ERROR, "Not enough headroom for NS header\n");
405 msgb_free(msg);
406 return -EIO;
407 }
408
409 nsh->pdu_type = NS_PDUT_UNITDATA;
410 nsh->data[0] = sducontrol;
411 nsh->data[1] = bvci >> 8;
412 nsh->data[2] = bvci & 0xff;
413
414 return nsvc->bind->send_vc(nsvc, msg);
415}
416
417/*! Transmit a NS-STATUS on a given NSVC
418 * \param[in] nsvc NS-VC to be used for transmission
419 * \param[in] cause Numeric NS cause value
420 * \param[in] bvci BVCI to be reset within NSVC
421 * \param[in] orig_msg message causing the STATUS */
422int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
423 uint16_t bvci, struct msgb *orig_msg)
424{
425 struct msgb *msg = gprs_ns2_msgb_alloc();
426 struct gprs_ns_hdr *nsh;
427 uint16_t nsvci = osmo_htons(nsvc->nsvci);
428
429 log_set_context(LOG_CTX_GB_NSVC, nsvc);
430
431 bvci = osmo_htons(bvci);
432
433 if (!msg)
434 return -ENOMEM;
435
436 LOGP(DLNS, LOGL_NOTICE, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n",
437 nsvc->nse->nsei, nsvc->nsvci, gprs_ns2_cause_str(cause));
438
439 msg->l2h = msgb_put(msg, sizeof(*nsh));
440 nsh = (struct gprs_ns_hdr *) msg->l2h;
441 nsh->pdu_type = NS_PDUT_STATUS;
442
443 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
444
445 /* Section 9.2.7.1: Static conditions for NS-VCI */
446 if (cause == NS_CAUSE_NSVC_BLOCKED ||
447 cause == NS_CAUSE_NSVC_UNKNOWN)
448 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
449
450 /* Section 9.2.7.2: Static conditions for NS PDU */
451 switch (cause) {
452 case NS_CAUSE_SEM_INCORR_PDU:
453 case NS_CAUSE_PDU_INCOMP_PSTATE:
454 case NS_CAUSE_PROTO_ERR_UNSPEC:
455 case NS_CAUSE_INVAL_ESSENT_IE:
456 case NS_CAUSE_MISSING_ESSENT_IE:
457 msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg),
458 orig_msg->l2h);
459 break;
460 default:
461 break;
462 }
463
464 /* Section 9.2.7.3: Static conditions for BVCI */
465 if (cause == NS_CAUSE_BVCI_UNKNOWN)
466 msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci);
467
468 return nsvc->bind->send_vc(nsvc, msg);
469}
470
471
472/*! Encode + Transmit a SNS-ACK as per Section 9.3.1.
473 * \param[in] nsvc NS-VC through which to transmit the ACK
474 * \param[in] trans_id Transaction ID which to acknowledge
475 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
476 * \param[in] ip4_elems Array of IPv4 Elements
477 * \param[in] num_ip4_elems number of ip4_elems
478 * \returns 0 on success; negative in case of error */
479int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
480 const struct gprs_ns_ie_ip4_elem *ip4_elems,
481 unsigned int num_ip4_elems,
482 const struct gprs_ns_ie_ip6_elem *ip6_elems,
483 unsigned int num_ip6_elems)
484{
485 struct msgb *msg = gprs_ns2_msgb_alloc();
486 struct gprs_ns_hdr *nsh;
487 uint16_t nsei;
488
489 if (!nsvc)
490 return -1;
491
492 msg = gprs_ns2_msgb_alloc();
493
494 log_set_context(LOG_CTX_GB_NSVC, nsvc);
495 if (!msg)
496 return -ENOMEM;
497
498 if (!nsvc->nse->bss_sns_fi) {
499 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
500 nsvc->nse->nsei);
501 msgb_free(msg);
502 return -EIO;
503 }
504
505 nsei = osmo_htons(nsvc->nse->nsei);
506
507 msg->l2h = msgb_put(msg, sizeof(*nsh));
508 nsh = (struct gprs_ns_hdr *) msg->l2h;
509
510 nsh->pdu_type = SNS_PDUT_ACK;
511 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
512 msgb_v_put(msg, trans_id);
513 if (cause)
514 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
515 if (ip4_elems) {
516 /* List of IP4 Elements 10.3.2c */
517 msgb_tvlv_put(msg, NS_IE_IPv4_LIST,
518 num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
519 (const uint8_t *)ip4_elems);
520 }
521 if (ip6_elems) {
522 /* List of IP6 elements 10.3.2d */
523 msgb_tvlv_put(msg, NS_IE_IPv6_LIST,
524 num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
525 (const uint8_t *)ip6_elems);
526 }
527
528 return nsvc->bind->send_vc(nsvc, msg);
529}
530
531/*! Encode + Transmit a SNS-CONFIG as per Section 9.3.4.
532 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
533 * \param[in] end_flag Whether or not this is the last SNS-CONFIG
534 * \param[in] ip4_elems Array of IPv4 Elements
535 * \param[in] num_ip4_elems number of ip4_elems
536 * \returns 0 on success; negative in case of error */
537int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
538 const struct gprs_ns_ie_ip4_elem *ip4_elems,
539 unsigned int num_ip4_elems,
540 const struct gprs_ns_ie_ip6_elem *ip6_elems,
541 unsigned int num_ip6_elems)
542{
543 struct msgb *msg;
544 struct gprs_ns_hdr *nsh;
545 uint16_t nsei;
546
547 if (!nsvc)
548 return -1;
549
550 msg = gprs_ns2_msgb_alloc();
551
552 log_set_context(LOG_CTX_GB_NSVC, nsvc);
553 if (!msg)
554 return -ENOMEM;
555
556 if (!nsvc->nse->bss_sns_fi) {
557 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
558 nsvc->nse->nsei);
559 msgb_free(msg);
560 return -EIO;
561 }
562
563 nsei = osmo_htons(nsvc->nse->nsei);
564
565 msg->l2h = msgb_put(msg, sizeof(*nsh));
566 nsh = (struct gprs_ns_hdr *) msg->l2h;
567
568 nsh->pdu_type = SNS_PDUT_CONFIG;
569
570 msgb_v_put(msg, end_flag ? 0x01 : 0x00);
571 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
572
573 /* List of IP4 Elements 10.3.2c */
574 if (ip4_elems) {
575 msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
576 (const uint8_t *)ip4_elems);
577 } else if (ip6_elems) {
578 /* List of IP6 elements 10.3.2d */
579 msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
580 (const uint8_t *)ip6_elems);
581 }
582
583 return nsvc->bind->send_vc(nsvc, msg);
584}
585
586/*! Encode + Transmit a SNS-CONFIG-ACK as per Section 9.3.5.
587 * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG-ACK
588 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
589 * \returns 0 on success; negative in case of error */
590int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
591{
592 struct msgb *msg;
593 struct gprs_ns_hdr *nsh;
594 uint16_t nsei;
595
596 if (!nsvc)
597 return -1;
598
599 msg = gprs_ns2_msgb_alloc();
600 log_set_context(LOG_CTX_GB_NSVC, nsvc);
601 if (!msg)
602 return -ENOMEM;
603
604 if (!nsvc->nse->bss_sns_fi) {
605 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
606 nsvc->nse->nsei);
607 msgb_free(msg);
608 return -EIO;
609 }
610
611 nsei = osmo_htons(nsvc->nse->nsei);
612
613 msg->l2h = msgb_put(msg, sizeof(*nsh));
614 nsh = (struct gprs_ns_hdr *) msg->l2h;
615
616 nsh->pdu_type = SNS_PDUT_CONFIG_ACK;
617
618 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
619 if (cause)
620 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
621
622 return nsvc->bind->send_vc(nsvc, msg);
623}
624
625
626/*! Encode + transmit a SNS-SIZE as per Section 9.3.7.
627 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE
628 * \param[in] reset_flag Whether or not to add a RESET flag
629 * \param[in] max_nr_nsvc Maximum number of NS-VCs
630 * \param[in] ip4_ep_nr Number of IPv4 endpoints (< 0 will omit the TLV)
631 * \param[in] ip6_ep_nr Number of IPv6 endpoints (< 0 will omit the TLV)
632 * \returns 0 on success; negative in case of error */
633int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_nsvc,
634 int ip4_ep_nr, int ip6_ep_nr)
635{
636 struct msgb *msg = gprs_ns2_msgb_alloc();
637 struct gprs_ns_hdr *nsh;
638 uint16_t nsei;
639
640 if (!nsvc)
641 return -1;
642
643 msg = gprs_ns2_msgb_alloc();
644
645 log_set_context(LOG_CTX_GB_NSVC, nsvc);
646 if (!msg)
647 return -ENOMEM;
648
649 if (!nsvc->nse->bss_sns_fi) {
650 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
651 nsvc->nse->nsei);
652 msgb_free(msg);
653 return -EIO;
654 }
655
656 nsei = osmo_htons(nsvc->nse->nsei);
657
658 msg->l2h = msgb_put(msg, sizeof(*nsh));
659 nsh = (struct gprs_ns_hdr *) msg->l2h;
660
661 nsh->pdu_type = SNS_PDUT_SIZE;
662
663 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
664 msgb_tv_put(msg, NS_IE_RESET_FLAG, reset_flag ? 0x01 : 0x00);
665 msgb_tv16_put(msg, NS_IE_MAX_NR_NSVC, max_nr_nsvc);
666 if (ip4_ep_nr >= 0)
667 msgb_tv16_put(msg, NS_IE_IPv4_EP_NR, ip4_ep_nr);
668 if (ip6_ep_nr >= 0)
669 msgb_tv16_put(msg, NS_IE_IPv6_EP_NR, ip6_ep_nr);
670
671 return nsvc->bind->send_vc(nsvc, msg);
672}
673
674/*! Encode + Transmit a SNS-SIZE-ACK as per Section 9.3.8.
675 * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE-ACK
676 * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
677 * \returns 0 on success; negative in case of error */
678int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
679{
680 struct msgb *msg = gprs_ns2_msgb_alloc();
681 struct gprs_ns_hdr *nsh;
682 uint16_t nsei;
683
684 log_set_context(LOG_CTX_GB_NSVC, nsvc);
685 if (!msg)
686 return -ENOMEM;
687
688 if (!nsvc->nse->bss_sns_fi) {
689 LOGP(DLNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
690 nsvc->nse->nsei);
691 msgb_free(msg);
692 return -EIO;
693 }
694
695 nsei = osmo_htons(nsvc->nse->nsei);
696
697 msg->l2h = msgb_put(msg, sizeof(*nsh));
698 nsh = (struct gprs_ns_hdr *) msg->l2h;
699
700 nsh->pdu_type = SNS_PDUT_SIZE_ACK;
701
702 msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
703 if (cause)
704 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
705
706 return nsvc->bind->send_vc(nsvc, msg);
707}
708
709