blob: 6ddc58b6c40a34c0b1e609d3da1a2f736e825459 [file] [log] [blame]
Alexander Couzens6a161492020-07-12 13:45:50 +02001/*! \file gprs_ns2.c
2 * GPRS Networks Service (NS) messages on the Gb interface.
3 * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
4 * as well as its successor 3GPP TS 48.016 */
5
6/* (C) 2009-2018 by Harald Welte <laforge@gnumonks.org>
7 * (C) 2016-2017,2020 sysmocom - s.f.m.c. GmbH
8 * Author: Alexander Couzens <lynxis@fe80.eu>
9 *
10 *
11 * All Rights Reserved
12 *
13 * SPDX-License-Identifier: GPL-2.0+
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 *
28 */
29
30/*! \addtogroup libgb
31 * @{
32 *
33 * GPRS Networks Service (NS) messages on the Gb interface
34 * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
35 *
36 * Some introduction into NS: NS is used typically on top of frame relay,
37 * but in the ip.access world it is encapsulated in UDP packets. It serves
38 * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't
39 * do much, apart from providing congestion notification and status indication.
40 *
41 * Terms:
42 *
43 * NS Network Service
44 * NSVC NS Virtual Connection
45 * NSEI NS Entity Identifier
46 * NSVL NS Virtual Link
47 * NSVLI NS Virtual Link Identifier
48 * BVC BSSGP Virtual Connection
49 * BVCI BSSGP Virtual Connection Identifier
50 * NSVCG NS Virtual Connection Goup
51 * Blocked NS-VC cannot be used for user traffic
52 * Alive Ability of a NS-VC to provide communication
53 *
54 * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will
55 * therefore identify the BSSGP virtual connection by a BVCI passed down to NS.
56 * NS then has to figure out which NSVC's are responsible for this BVCI.
57 * Those mappings are administratively configured.
58 *
59 * This implementation has the following limitations:
60 * - Only one NS-VC for each NSE: No load-sharing function
61 * - NSVCI 65535 and 65534 are reserved for internal use
62 * - Only UDP is supported as of now, no frame relay support
63 * - There are no BLOCK and UNBLOCK timers (yet?)
64 *
65 * \file gprs_ns2.c */
66
67#include <stdlib.h>
68#include <unistd.h>
69#include <errno.h>
70#include <stdint.h>
71
72#include <sys/types.h>
73#include <sys/socket.h>
74#include <arpa/inet.h>
75
76#include <osmocom/core/fsm.h>
77#include <osmocom/core/msgb.h>
78#include <osmocom/core/rate_ctr.h>
79#include <osmocom/core/socket.h>
80#include <osmocom/core/sockaddr_str.h>
81#include <osmocom/core/stats.h>
82#include <osmocom/core/stat_item.h>
83#include <osmocom/core/talloc.h>
84#include <osmocom/gprs/gprs_msgb.h>
85#include <osmocom/gsm/prim.h>
86#include <osmocom/gsm/tlv.h>
87
88#include "gprs_ns2_internal.h"
89
90#define ns_set_state(ns_, st_) ns_set_state_with_log(ns_, st_, false, __FILE__, __LINE__)
91#define ns_set_remote_state(ns_, st_) ns_set_state_with_log(ns_, st_, true, __FILE__, __LINE__)
92#define ns_mark_blocked(ns_) ns_set_state(ns_, (ns_)->state | NSE_S_BLOCKED)
93#define ns_mark_unblocked(ns_) ns_set_state(ns_, (ns_)->state & (~NSE_S_BLOCKED));
94#define ns_mark_alive(ns_) ns_set_state(ns_, (ns_)->state | NSE_S_ALIVE)
95#define ns_mark_dead(ns_) ns_set_state(ns_, (ns_)->state & (~NSE_S_ALIVE));
96
97/* HACK: The NS_IE_IP_ADDR does not follow any known TLV rules.
98 * Since it's a hard ABI break to implement 16 bit tag with fixed length entries to workaround it,
99 * the parser will be called with ns_att_tlvdef1 and if it's failed with ns_att_tlvdef2.
100 * The TLV parser depends on 8bit tag in many places.
101 * The NS_IE_IP_ADDR is only valid for SNS_ACK SNS_ADD and SNS_DELETE.
102 */
103static const struct tlv_definition ns_att_tlvdef1 = {
104 .def = {
105 [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 },
106 [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 },
107 [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 },
108 [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 },
109 [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 },
110 [NS_IE_IPv4_LIST] = { TLV_TYPE_TvLV, 0 },
111 [NS_IE_IPv6_LIST] = { TLV_TYPE_TvLV, 0 },
112 [NS_IE_MAX_NR_NSVC] = { TLV_TYPE_FIXED, 2 },
113 [NS_IE_IPv4_EP_NR] = { TLV_TYPE_FIXED, 2 },
114 [NS_IE_IPv6_EP_NR] = { TLV_TYPE_FIXED, 2 },
115 [NS_IE_RESET_FLAG] = { TLV_TYPE_TV, 0 },
116 /* NS_IE_IP_ADDR in the IPv4 version */
117 [NS_IE_IP_ADDR] = { TLV_TYPE_FIXED, 5 },
118 },
119};
120
121static const struct tlv_definition ns_att_tlvdef2 = {
122 .def = {
123 [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 },
124 [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 },
125 [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 },
126 [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 },
127 [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 },
128 [NS_IE_IPv4_LIST] = { TLV_TYPE_TvLV, 0 },
129 [NS_IE_IPv6_LIST] = { TLV_TYPE_TvLV, 0 },
130 [NS_IE_MAX_NR_NSVC] = { TLV_TYPE_FIXED, 2 },
131 [NS_IE_IPv4_EP_NR] = { TLV_TYPE_FIXED, 2 },
132 [NS_IE_IPv6_EP_NR] = { TLV_TYPE_FIXED, 2 },
133 [NS_IE_RESET_FLAG] = { TLV_TYPE_TV, 0 },
134 /* NS_IE_IP_ADDR in the IPv6 version */
135 [NS_IE_IP_ADDR] = { TLV_TYPE_FIXED, 17 },
136 },
137};
138
139
140/* Section 10.3.2, Table 13 */
Alexander Couzensb3b837c2020-10-27 15:12:25 +0100141const struct value_string gprs_ns2_cause_strs[] = {
Alexander Couzens6a161492020-07-12 13:45:50 +0200142 { NS_CAUSE_TRANSIT_FAIL, "Transit network failure" },
143 { NS_CAUSE_OM_INTERVENTION, "O&M intervention" },
144 { NS_CAUSE_EQUIP_FAIL, "Equipment failure" },
145 { NS_CAUSE_NSVC_BLOCKED, "NS-VC blocked" },
146 { NS_CAUSE_NSVC_UNKNOWN, "NS-VC unknown" },
147 { NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" },
148 { NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" },
149 { NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" },
150 { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" },
151 { NS_CAUSE_INVAL_ESSENT_IE, "Invalid essential IE" },
152 { NS_CAUSE_MISSING_ESSENT_IE, "Missing essential IE" },
153 { NS_CAUSE_INVAL_NR_IPv4_EP, "Invalid Number of IPv4 Endpoints" },
154 { NS_CAUSE_INVAL_NR_IPv6_EP, "Invalid Number of IPv6 Endpoints" },
155 { NS_CAUSE_INVAL_NR_NS_VC, "Invalid Number of NS-VCs" },
156 { NS_CAUSE_INVAL_WEIGH, "Invalid Weights" },
157 { NS_CAUSE_UNKN_IP_EP, "Unknown IP Endpoint" },
158 { NS_CAUSE_UNKN_IP_ADDR, "Unknown IP Address" },
159 { NS_CAUSE_UNKN_IP_TEST_FAILED, "IP Test Failed" },
160 { 0, NULL }
161};
162
Alexander Couzens6a161492020-07-12 13:45:50 +0200163static const struct rate_ctr_desc nsvc_ctr_description[] = {
164 { "packets:in", "Packets at NS Level ( In)" },
165 { "packets:out","Packets at NS Level (Out)" },
166 { "bytes:in", "Bytes at NS Level ( In)" },
167 { "bytes:out", "Bytes at NS Level (Out)" },
168 { "blocked", "NS-VC Block count " },
169 { "dead", "NS-VC gone dead count " },
170 { "replaced", "NS-VC replaced other count" },
171 { "nsei-chg", "NS-VC changed NSEI count " },
172 { "inv-nsvci", "NS-VCI was invalid count " },
173 { "inv-nsei", "NSEI was invalid count " },
174 { "lost:alive", "ALIVE ACK missing count " },
175 { "lost:reset", "RESET ACK missing count " },
176};
177
178static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
179 .group_name_prefix = "ns:nsvc",
180 .group_description = "NSVC Peer Statistics",
181 .num_ctr = ARRAY_SIZE(nsvc_ctr_description),
182 .ctr_desc = nsvc_ctr_description,
183 .class_id = OSMO_STATS_CLASS_PEER,
184};
185
186
187static const struct osmo_stat_item_desc nsvc_stat_description[] = {
188 { "alive.delay", "ALIVE response time ", "ms", 16, 0 },
189};
190
191static const struct osmo_stat_item_group_desc nsvc_statg_desc = {
192 .group_name_prefix = "ns.nsvc",
193 .group_description = "NSVC Peer Statistics",
194 .num_items = ARRAY_SIZE(nsvc_stat_description),
195 .item_desc = nsvc_stat_description,
196 .class_id = OSMO_STATS_CLASS_PEER,
197};
198
Alexander Couzens2498f1d2020-10-27 01:09:01 +0100199const struct value_string gprs_ns2_aff_cause_prim_strs[] = {
200 { NS_AFF_CAUSE_VC_FAILURE, "NSVC failure" },
201 { NS_AFF_CAUSE_VC_RECOVERY, "NSVC recovery" },
202 { NS_AFF_CAUSE_FAILURE, "NSE failure" },
203 { NS_AFF_CAUSE_RECOVERY, "NSE recovery" },
204 { NS_AFF_CAUSE_SNS_CONFIGURED, "NSE SNS configured" },
205 { NS_AFF_CAUSE_SNS_FAILURE, "NSE SNS failure" },
206 { 0, NULL }
207};
208
209const struct value_string ns2_prim_str[] = {
210 { PRIM_NS_UNIT_DATA, "UNIT DATA" },
211 { PRIM_NS_CONGESTION, "CONGESTION" },
212 { PRIM_NS_STATUS, "STATUS" },
213 { 0, NULL }
214};
215
Harald Welte5bef2cc2020-09-18 22:33:24 +0200216/*! string-format a given NS-VC into a user-supplied buffer.
217 * \param[in] buf user-allocated output buffer
218 * \param[in] buf_len size of user-allocated output buffer in bytes
219 * \param[in] nsvc NS-VC to be string-formatted
220 * \return pointer to buf on success; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200221char *gprs_ns2_ll_str_buf(char *buf, size_t buf_len, struct gprs_ns2_vc *nsvc)
222{
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200223 const struct osmo_sockaddr *local;
224 const struct osmo_sockaddr *remote;
Alexander Couzens6a161492020-07-12 13:45:50 +0200225 struct osmo_sockaddr_str local_str;
226 struct osmo_sockaddr_str remote_str;
227
228 if (!buf_len)
Harald Welte92ad0292020-09-18 22:34:24 +0200229 return NULL;
Alexander Couzens6a161492020-07-12 13:45:50 +0200230
231 switch (nsvc->ll) {
232 case GPRS_NS_LL_UDP:
233 if (!gprs_ns2_is_ip_bind(nsvc->bind)) {
234 buf[0] = '\0';
235 return buf;
236 }
237
238 local = gprs_ns2_ip_bind_sockaddr(nsvc->bind);
Alexander Couzensc4229a42020-10-11 20:58:04 +0200239 remote = gprs_ns2_ip_vc_remote(nsvc);
Alexander Couzens6a161492020-07-12 13:45:50 +0200240 if (osmo_sockaddr_str_from_sockaddr(&local_str, &local->u.sas))
241 strcpy(local_str.ip, "invalid");
242 if (osmo_sockaddr_str_from_sockaddr(&remote_str, &remote->u.sas))
243 strcpy(remote_str.ip, "invalid");
244
245 if (nsvc->nsvci_is_valid)
246 snprintf(buf, buf_len, "udp)[%s]:%u<%u>[%s]:%u",
247 local_str.ip, local_str.port,
248 nsvc->nsvci,
249 remote_str.ip, remote_str.port);
250 else
251 snprintf(buf, buf_len, "udp)[%s]:%u<>[%s]:%u",
252 local_str.ip, local_str.port,
253 remote_str.ip, remote_str.port);
254 break;
255 case GPRS_NS_LL_FR_GRE:
256 snprintf(buf, buf_len, "frgre)");
257 break;
258 case GPRS_NS_LL_E1:
259 snprintf(buf, buf_len, "e1)");
260 break;
261 default:
262 buf[0] = '\0';
263 break;
264 }
265
266 buf[buf_len - 1] = '\0';
267
268 return buf;
269}
270
271/* udp is the longest: udp)[IP6]:65536<65536>[IP6]:65536 */
272#define NS2_LL_MAX_STR 4+2*(INET6_ADDRSTRLEN+9)+8
273
Harald Welte5bef2cc2020-09-18 22:33:24 +0200274/*! string-format a given NS-VC to a thread-local static buffer.
275 * \param[in] nsvc NS-VC to be string-formatted
276 * \return pointer to the string on success; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200277const char *gprs_ns2_ll_str(struct gprs_ns2_vc *nsvc)
278{
279 static __thread char buf[NS2_LL_MAX_STR];
280 return gprs_ns2_ll_str_buf(buf, sizeof(buf), nsvc);
281}
282
Harald Welte5bef2cc2020-09-18 22:33:24 +0200283/*! string-format a given NS-VC to a dynamically allocated string.
284 * \param[in] ctx talloc context from which to allocate
285 * \param[in] nsvc NS-VC to be string-formatted
286 * \return pointer to the string on success; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200287char *gprs_ns2_ll_str_c(const void *ctx, struct gprs_ns2_vc *nsvc)
288{
289 char *buf = talloc_size(ctx, NS2_LL_MAX_STR);
290 if (!buf)
291 return buf;
292 return gprs_ns2_ll_str_buf(buf, NS2_LL_MAX_STR, nsvc);
293}
294
Daniel Willmannf1286542020-11-03 23:03:33 +0100295/*! Return the current state name of a given NS-VC to a thread-local static buffer.
296 * \param[in] nsvc NS-VC to return the state of
297 * \return pointer to the string on success; NULL on error */
298const char *gprs_ns2_nsvc_state_name(struct gprs_ns2_vc *nsvc)
299{
300 return osmo_fsm_inst_state_name(nsvc->fi);
301}
302
Harald Welte5bef2cc2020-09-18 22:33:24 +0200303/*! Receive a primitive from the NS User (Gb).
304 * \param[in] nsi NS instance to which the primitive is issued
305 * \param[in] oph The primitive
306 * \return 0 on success; negative on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200307int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph)
308{
309 /* TODO: implement load distribution function */
310 /* TODO: implement resource distribution */
311 /* TODO: check for empty PDUs which can be sent to Request/Confirm
312 * the IP endpoint */
313 struct osmo_gprs_ns2_prim *nsp;
314 struct gprs_ns2_nse *nse = NULL;
315 struct gprs_ns2_vc *nsvc = NULL, *tmp;
316 uint16_t bvci, nsei;
317 uint8_t sducontrol = 0;
318
319 if (oph->sap != SAP_NS)
320 return -EINVAL;
321
322 nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
323
324 if (oph->operation != PRIM_OP_REQUEST || oph->primitive != PRIM_NS_UNIT_DATA)
325 return -EINVAL;
326
327 if (!oph->msg)
328 return -EINVAL;
329
330 bvci = nsp->bvci;
331 nsei = nsp->nsei;
332
333 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
334 if (!nse)
335 return -EINVAL;
336
337 llist_for_each_entry(tmp, &nse->nsvc, list) {
338 if (!gprs_ns2_vc_is_unblocked(tmp))
339 continue;
340 if (bvci == 0 && tmp->sig_weight == 0)
341 continue;
342 if (bvci != 0 && tmp->data_weight == 0)
343 continue;
344
345 nsvc = tmp;
346 }
347
348 /* TODO: send a status primitive back */
349 if (!nsvc)
350 return 0;
351
352 if (nsp->u.unitdata.change == NS_ENDPOINT_REQUEST_CHANGE)
353 sducontrol = 1;
354 else if (nsp->u.unitdata.change == NS_ENDPOINT_CONFIRM_CHANGE)
355 sducontrol = 2;
356
357 return ns2_tx_unit_data(nsvc, bvci, sducontrol, oph->msg);
358}
359
Harald Welte5bef2cc2020-09-18 22:33:24 +0200360/*! Send a STATUS.ind primitive to the specified NS instance user.
361 * \param[in] nsi NS instance on which we operate
362 * \param[in] nsei NSEI to which the statue relates
363 * \param[in] bvci BVCI to which the status relates
364 * \param[in] cause The cause of the status */
Alexander Couzensbf95f0f2020-10-01 22:56:03 +0200365void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
Daniel Willmann15c09a82020-11-03 23:05:43 +0100366 struct gprs_ns2_vc *nsvc,
Alexander Couzensbf95f0f2020-10-01 22:56:03 +0200367 uint16_t bvci,
Alexander Couzens6a161492020-07-12 13:45:50 +0200368 enum gprs_ns2_affecting_cause cause)
369{
Daniel Willmann15c09a82020-11-03 23:05:43 +0100370 char nsvc_str[NS2_LL_MAX_STR];
Alexander Couzens6a161492020-07-12 13:45:50 +0200371 struct osmo_gprs_ns2_prim nsp = {};
Alexander Couzensbf95f0f2020-10-01 22:56:03 +0200372 nsp.nsei = nse->nsei;
Alexander Couzens6a161492020-07-12 13:45:50 +0200373 nsp.bvci = bvci;
374 nsp.u.status.cause = cause;
375 nsp.u.status.transfer = -1;
Alexander Couzensda0a2852020-10-01 23:24:07 +0200376 nsp.u.status.first = nse->first;
377 nsp.u.status.persistent = nse->persistent;
Daniel Willmann15c09a82020-11-03 23:05:43 +0100378 if (nsvc)
379 nsp.u.status.nsvc = gprs_ns2_ll_str_buf(nsvc_str, sizeof(nsvc_str), nsvc);
380
Alexander Couzens6a161492020-07-12 13:45:50 +0200381 osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_STATUS,
382 PRIM_OP_INDICATION, NULL);
Alexander Couzensbf95f0f2020-10-01 22:56:03 +0200383 nse->nsi->cb(&nsp.oph, nse->nsi->cb_data);
Alexander Couzens6a161492020-07-12 13:45:50 +0200384}
385
Harald Welte5bef2cc2020-09-18 22:33:24 +0200386/*! Allocate a NS-VC within the given bind + NSE.
387 * \param[in] bind The 'bind' on which we operate
388 * \param[in] nse The NS Entity on which we operate
389 * \param[in] initiater - if this is an incoming remote (!initiater) or a local outgoing connection (initater)
390 * \return newly allocated NS-VC on success; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200391struct gprs_ns2_vc *ns2_vc_alloc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_nse *nse, bool initiater)
392{
393 struct gprs_ns2_vc *nsvc = talloc_zero(bind, struct gprs_ns2_vc);
394
395 if (!nsvc)
396 return NULL;
397
398 nsvc->bind = bind;
399 nsvc->nse = nse;
400 nsvc->mode = bind->vc_mode;
401 nsvc->sig_weight = 1;
402 nsvc->data_weight = 1;
403
404 nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, bind->nsi->rate_ctr_idx);
405 if (!nsvc->ctrg) {
406 goto err;
407 }
408 nsvc->statg = osmo_stat_item_group_alloc(nsvc, &nsvc_statg_desc, bind->nsi->rate_ctr_idx);
409 if (!nsvc->statg)
410 goto err_group;
411 if (!gprs_ns2_vc_fsm_alloc(nsvc, NULL, initiater))
412 goto err_statg;
413
414 bind->nsi->rate_ctr_idx++;
415
416 llist_add(&nsvc->list, &nse->nsvc);
417 llist_add(&nsvc->blist, &bind->nsvc);
418
419 return nsvc;
420
421err_statg:
422 osmo_stat_item_group_free(nsvc->statg);
423err_group:
424 rate_ctr_group_free(nsvc->ctrg);
425err:
426 talloc_free(nsvc);
427
428 return NULL;
429}
430
Harald Welte5bef2cc2020-09-18 22:33:24 +0200431/*! Destroy/release given NS-VC.
432 * \param[in] nsvc NS-VC to destroy */
Alexander Couzens6a161492020-07-12 13:45:50 +0200433void gprs_ns2_free_nsvc(struct gprs_ns2_vc *nsvc)
434{
435 if (!nsvc)
436 return;
437
Daniel Willmann15c09a82020-11-03 23:05:43 +0100438 ns2_prim_status_ind(nsvc->nse, nsvc, 0, NS_AFF_CAUSE_VC_FAILURE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200439
440 llist_del(&nsvc->list);
441 llist_del(&nsvc->blist);
442
443 /* notify nse this nsvc is unavailable */
444 ns2_nse_notify_unblocked(nsvc, false);
445
446 /* check if sns is using this VC */
447 ns2_sns_free_nsvc(nsvc);
448 osmo_fsm_inst_term(nsvc->fi, OSMO_FSM_TERM_REQUEST, NULL);
449
450 /* let the driver/bind clean up it's internal state */
451 if (nsvc->priv && nsvc->bind->free_vc)
452 nsvc->bind->free_vc(nsvc);
453
454 osmo_stat_item_group_free(nsvc->statg);
455 rate_ctr_group_free(nsvc->ctrg);
456
457 talloc_free(nsvc);
458}
459
Harald Welte5bef2cc2020-09-18 22:33:24 +0200460/*! Allocate a message buffer for use with the NS2 stack. */
Alexander Couzens6a161492020-07-12 13:45:50 +0200461struct msgb *gprs_ns2_msgb_alloc(void)
462{
463 struct msgb *msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM,
464 "GPRS/NS");
465 if (!msg) {
466 LOGP(DLNS, LOGL_ERROR, "Failed to allocate NS message of size %d\n",
467 NS_ALLOC_SIZE);
468 }
469 return msg;
470}
471
Harald Welte5bef2cc2020-09-18 22:33:24 +0200472/*! Create a status message to be sent over a new connection.
473 * \param[in] orig_msg the original message
474 * \param[in] tp TLVP parsed of the original message
475 * \param[out] reject callee-allocated message buffer of the generated NS-STATUS
476 * \param[in] cause Cause for the rejection
477 * \return 0 on success */
Alexander Couzens6a161492020-07-12 13:45:50 +0200478static int reject_status_msg(struct msgb *orig_msg, struct tlv_parsed *tp, struct msgb **reject, enum ns_cause cause)
479{
480 struct msgb *msg = gprs_ns2_msgb_alloc();
481 struct gprs_ns_hdr *nsh;
482 bool have_vci = false;
483 uint8_t _cause = cause;
484 uint16_t nsei = 0;
485
486 if (!msg)
487 return -ENOMEM;
488
489 if (TLVP_PRESENT(tp, NS_IE_NSEI)) {
490 nsei = tlvp_val16be(tp, NS_IE_NSEI);
491
492 LOGP(DLNS, LOGL_NOTICE, "NSEI=%u Rejecting message without NSVCI. Tx NS STATUS (cause=%s)\n",
493 nsei, gprs_ns2_cause_str(cause));
494 }
495
496 msg->l2h = msgb_put(msg, sizeof(*nsh));
497 nsh = (struct gprs_ns_hdr *) msg->l2h;
498 nsh->pdu_type = NS_PDUT_STATUS;
499
500 msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &_cause);
501 have_vci = TLVP_PRESENT(tp, NS_IE_VCI);
502
503 /* Section 9.2.7.1: Static conditions for NS-VCI */
504 if (cause == NS_CAUSE_NSVC_BLOCKED ||
505 cause == NS_CAUSE_NSVC_UNKNOWN) {
506 if (!have_vci) {
507 msgb_free(msg);
508 return -EINVAL;
509 }
510
511 msgb_tvlv_put(msg, NS_IE_VCI, 2, TLVP_VAL(tp, NS_IE_VCI));
512 }
513
514 /* Section 9.2.7.2: Static conditions for NS PDU */
515 switch (cause) {
516 case NS_CAUSE_SEM_INCORR_PDU:
517 case NS_CAUSE_PDU_INCOMP_PSTATE:
518 case NS_CAUSE_PROTO_ERR_UNSPEC:
519 case NS_CAUSE_INVAL_ESSENT_IE:
520 case NS_CAUSE_MISSING_ESSENT_IE:
521 msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg),
522 orig_msg->l2h);
523 break;
524 default:
525 break;
526 }
527
528 *reject = msg;
529 return 0;
530}
531
Harald Welte5bef2cc2020-09-18 22:33:24 +0200532/*! Resolve a NS Entity based on its NSEI.
533 * \param[in] nsi NS Instance in which we do the look-up
534 * \param[in] nsei NSEI to look up
535 * \return NS Entity in successful case; NULL if none found */
Alexander Couzens6a161492020-07-12 13:45:50 +0200536struct gprs_ns2_nse *gprs_ns2_nse_by_nsei(struct gprs_ns2_inst *nsi, uint16_t nsei)
537{
538 struct gprs_ns2_nse *nse;
539
540 llist_for_each_entry(nse, &nsi->nse, list) {
541 if (nse->nsei == nsei)
542 return nse;
543 }
544
545 return NULL;
546}
547
Harald Welte5bef2cc2020-09-18 22:33:24 +0200548/*! Resolve a NS-VC Entity based on its NS-VCI.
549 * \param[in] nsi NS Instance in which we do the look-up
550 * \param[in] nsvci NS-VCI to look up
551 * \return NS-VC Entity in successful case; NULL if none found */
Alexander Couzens6a161492020-07-12 13:45:50 +0200552struct gprs_ns2_vc *gprs_ns2_nsvc_by_nsvci(struct gprs_ns2_inst *nsi, uint16_t nsvci)
553{
554 struct gprs_ns2_nse *nse;
555 struct gprs_ns2_vc *nsvc;
556
557 llist_for_each_entry(nse, &nsi->nse, list) {
558 llist_for_each_entry(nsvc, &nse->nsvc, list) {
559 if (nsvc->nsvci_is_valid && nsvc->nsvci == nsvci)
560 return nsvc;
561 }
562 }
563
564 return NULL;
565}
566
Harald Welte5bef2cc2020-09-18 22:33:24 +0200567/*! Create a NS Entity within given NS instance.
568 * \param[in] nsi NS instance in which to create NS Entity
569 * \param[in] nsei NS Entity Identifier of to-be-created NSE
570 * \returns newly-allocated NS-E in successful case; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200571struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nsei)
572{
573 struct gprs_ns2_nse *nse;
574
575 nse = gprs_ns2_nse_by_nsei(nsi, nsei);
576 if (nse) {
577 LOGP(DLNS, LOGL_ERROR, "NSEI:%u Can not create a NSE with already taken NSEI\n", nsei);
578 return nse;
579 }
580
581 nse = talloc_zero(nsi, struct gprs_ns2_nse);
582 if (!nse)
583 return NULL;
584
585 nse->nsei = nsei;
586 nse->nsi = nsi;
Alexander Couzensda0a2852020-10-01 23:24:07 +0200587 nse->first = true;
Alexander Couzens6a161492020-07-12 13:45:50 +0200588 llist_add(&nse->list, &nsi->nse);
589 INIT_LLIST_HEAD(&nse->nsvc);
590
591 return nse;
592}
593
Alexander Couzens05e7f7d2020-10-11 19:51:46 +0200594/*! Return the NSEI
595 * \param[in] nse NS Entity
596 * \return the nsei.
597 */
598uint16_t gprs_ns2_nse_nsei(struct gprs_ns2_nse *nse)
599{
600 return nse->nsei;
601}
602
Harald Welte5bef2cc2020-09-18 22:33:24 +0200603/*! Destroy given NS Entity.
604 * \param[in] nse NS Entity to destroy */
Alexander Couzens6a161492020-07-12 13:45:50 +0200605void gprs_ns2_free_nse(struct gprs_ns2_nse *nse)
606{
607 struct gprs_ns2_vc *nsvc, *tmp;
608
609 if (!nse)
610 return;
611
612 llist_for_each_entry_safe(nsvc, tmp, &nse->nsvc, list) {
613 gprs_ns2_free_nsvc(nsvc);
614 }
615
Daniel Willmann15c09a82020-11-03 23:05:43 +0100616 ns2_prim_status_ind(nse, NULL, 0, NS_AFF_CAUSE_FAILURE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200617
618 llist_del(&nse->list);
619 if (nse->bss_sns_fi)
620 osmo_fsm_inst_term(nse->bss_sns_fi, OSMO_FSM_TERM_REQUEST, NULL);
621 talloc_free(nse);
622}
623
Alexander Couzens4b6c8af2020-10-11 20:15:25 +0200624void gprs_ns2_free_nses(struct gprs_ns2_inst *nsi)
625{
626 struct gprs_ns2_nse *nse, *ntmp;
627
628 llist_for_each_entry_safe(nse, ntmp, &nsi->nse, list) {
629 gprs_ns2_free_nse(nse);
630 }
631}
632
Alexander Couzens6a161492020-07-12 13:45:50 +0200633static inline int ns2_tlv_parse(struct tlv_parsed *dec,
634 const uint8_t *buf, int buf_len, uint8_t lv_tag,
635 uint8_t lv_tag2)
636{
637 /* workaround for NS_IE_IP_ADDR not following any known TLV rules.
638 * See comment of ns_att_tlvdef1. */
639 int rc = tlv_parse(dec, &ns_att_tlvdef1, buf, buf_len, lv_tag, lv_tag2);
640 if (rc < 0)
641 return tlv_parse(dec, &ns_att_tlvdef2, buf, buf_len, lv_tag, lv_tag2);
642 return rc;
643}
644
645
Harald Welte5bef2cc2020-09-18 22:33:24 +0200646/*! Create a new NS-VC based on a [received] message. Depending on the bind it might create a NSE.
647 * \param[in] bind the bind through which msg was received
648 * \param[in] msg the actual received message
649 * \param[in] logname A name to describe the VC. E.g. ip address pair
650 * \param[out] reject A message filled to be sent back. Only used in failure cases.
651 * \param[out] success A pointer which will be set to the new VC on success
652 * \return enum value indicating the status, e.g. GPRS_NS2_CS_CREATED */
Alexander Couzens6a161492020-07-12 13:45:50 +0200653enum gprs_ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
654 struct msgb *msg,
655 const char *logname,
656 struct msgb **reject,
657 struct gprs_ns2_vc **success)
658{
659 struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h;
660 struct tlv_parsed tp;
661 struct gprs_ns2_vc *nsvc;
662 struct gprs_ns2_nse *nse;
663 uint16_t nsvci;
664 uint16_t nsei;
665
666 int rc;
667
668 if (msg->len < sizeof(struct gprs_ns_hdr))
669 return GPRS_NS2_CS_ERROR;
670
671 if (nsh->pdu_type == NS_PDUT_STATUS) {
672 /* Do not respond, see 3GPP TS 08.16, 7.5.1 */
673 LOGP(DLNS, LOGL_INFO, "Ignoring NS STATUS from %s "
674 "for non-existing NS-VC\n",
675 logname);
676 return GPRS_NS2_CS_SKIPPED;
677 }
678
679 if (nsh->pdu_type == NS_PDUT_ALIVE_ACK) {
680 /* Ignore this, see 3GPP TS 08.16, 7.4.1 */
681 LOGP(DLNS, LOGL_INFO, "Ignoring NS ALIVE ACK from %s "
682 "for non-existing NS-VC\n",
683 logname);
684 return GPRS_NS2_CS_SKIPPED;
685 }
686
687 if (nsh->pdu_type == NS_PDUT_RESET_ACK) {
688 /* Ignore this, see 3GPP TS 08.16, 7.3.1 */
689 LOGP(DLNS, LOGL_INFO, "Ignoring NS RESET ACK from %s "
690 "for non-existing NS-VC\n",
691 logname);
692 return GPRS_NS2_CS_SKIPPED;
693 }
694
Alexander Couzens48f63862020-09-12 02:19:19 +0200695 if (bind->vc_mode == NS2_VC_MODE_BLOCKRESET) {
696 /* Only the RESET procedure creates a new NSVC */
697 if (nsh->pdu_type != NS_PDUT_RESET) {
698 rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_PDU_INCOMP_PSTATE);
Alexander Couzens6a161492020-07-12 13:45:50 +0200699
Alexander Couzens48f63862020-09-12 02:19:19 +0200700 if (rc < 0) {
701 LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
702 return rc;
703 }
704 return GPRS_NS2_CS_REJECTED;
Alexander Couzens6a161492020-07-12 13:45:50 +0200705 }
Alexander Couzens48f63862020-09-12 02:19:19 +0200706 } else { /* NS2_VC_MODE_ALIVE */
Alexander Couzens8ebc1ac2020-10-12 03:57:26 +0200707 rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_PDU_INCOMP_PSTATE);
Alexander Couzens48f63862020-09-12 02:19:19 +0200708
Alexander Couzens8ebc1ac2020-10-12 03:57:26 +0200709 if (rc < 0) {
710 LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
711 return rc;
Alexander Couzens48f63862020-09-12 02:19:19 +0200712 }
Alexander Couzens8ebc1ac2020-10-12 03:57:26 +0200713 return GPRS_NS2_CS_REJECTED;
Alexander Couzens6a161492020-07-12 13:45:50 +0200714 }
715
716 rc = ns2_tlv_parse(&tp, nsh->data,
717 msgb_l2len(msg) - sizeof(*nsh), 0, 0);
718 if (rc < 0) {
719 LOGP(DLNS, LOGL_ERROR, "Rx NS RESET Error %d during "
720 "TLV Parse\n", rc);
721 /* TODO: send invalid message back */
722 return GPRS_NS2_CS_REJECTED;
723 }
724
Alexander Couzens8ebc1ac2020-10-12 03:57:26 +0200725 if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) ||
726 !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) {
727 LOGP(DLNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
728 rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_MISSING_ESSENT_IE);
729 return GPRS_NS2_CS_REJECTED;
Alexander Couzens6a161492020-07-12 13:45:50 +0200730 }
731
732 /* find or create NSE */
733 nsei = tlvp_val16be(&tp, NS_IE_NSEI);
734 nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
735 if (!nse) {
736 if (!bind->nsi->create_nse) {
737 return GPRS_NS2_CS_SKIPPED;
738 }
739
740 nse = gprs_ns2_create_nse(bind->nsi, nsei);
741 if (!nse) {
742 return GPRS_NS2_CS_ERROR;
743 }
744 }
745
746 nsvc = ns2_vc_alloc(bind, nse, false);
747 if (!nsvc)
748 return GPRS_NS2_CS_SKIPPED;
749
750 nsvc->ll = GPRS_NS_LL_UDP;
751
752 nsvci = tlvp_val16be(&tp, NS_IE_VCI);
753 nsvc->nsvci = nsvci;
754 nsvc->nsvci_is_valid = true;
755
756 *success = nsvc;
757
758 return GPRS_NS2_CS_CREATED;
759}
760
Harald Welte5bef2cc2020-09-18 22:33:24 +0200761/*! Create, and connect an inactive, new IP-based NS-VC
762 * \param[in] bind bind in which the new NS-VC is to be created
763 * \param[in] remote remote address to which to connect
764 * \param[in] nse NS Entity in which the NS-VC is to be created
765 * \param[in] nsvci is only required when bind->vc_mode == NS2_VC_MODE_BLOCKRESET
766 * \return pointer to newly-allocated, connected and inactive NS-VC; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200767struct gprs_ns2_vc *gprs_ns2_ip_connect_inactive(struct gprs_ns2_vc_bind *bind,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700768 const struct osmo_sockaddr *remote,
Alexander Couzens6a161492020-07-12 13:45:50 +0200769 struct gprs_ns2_nse *nse,
770 uint16_t nsvci)
771{
772 struct gprs_ns2_vc *nsvc;
773
774 nsvc = gprs_ns2_ip_bind_connect(bind, nse, remote);
775 if (!nsvc)
776 return NULL;
777
778 if (nsvc->mode == NS2_VC_MODE_BLOCKRESET) {
779 nsvc->nsvci = nsvci;
780 nsvc->nsvci_is_valid = true;
781 }
782
783 return nsvc;
784}
785
Harald Welte5bef2cc2020-09-18 22:33:24 +0200786/*! Create, connect and activate a new IP-based NS-VC
787 * \param[in] bind bind in which the new NS-VC is to be created
788 * \param[in] remote remote address to which to connect
789 * \param[in] nse NS Entity in which the NS-VC is to be created
790 * \param[in] nsvci is only required when bind->vc_mode == NS2_VC_MODE_BLOCKRESET
791 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200792struct gprs_ns2_vc *gprs_ns2_ip_connect(struct gprs_ns2_vc_bind *bind,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700793 const struct osmo_sockaddr *remote,
Alexander Couzens6a161492020-07-12 13:45:50 +0200794 struct gprs_ns2_nse *nse,
795 uint16_t nsvci)
796{
797 struct gprs_ns2_vc *nsvc;
798 nsvc = gprs_ns2_ip_connect_inactive(bind, remote, nse, nsvci);
799 if (!nsvc)
800 return NULL;
801
802 gprs_ns2_vc_fsm_start(nsvc);
803
804 return nsvc;
805}
806
Harald Welte5bef2cc2020-09-18 22:33:24 +0200807/*! Create, connect and activate a new IP-based NS-VC
808 * \param[in] bind bind in which the new NS-VC is to be created
809 * \param[in] remote remote address to which to connect
810 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
811 * \param[in] nsvci is only required when bind->vc_mode == NS2_VC_MODE_BLOCKRESET
812 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200813struct gprs_ns2_vc *gprs_ns2_ip_connect2(struct gprs_ns2_vc_bind *bind,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700814 const struct osmo_sockaddr *remote,
Alexander Couzens6a161492020-07-12 13:45:50 +0200815 uint16_t nsei,
816 uint16_t nsvci)
817{
818 struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
819
820 if (!nse) {
821 nse = gprs_ns2_create_nse(bind->nsi, nsei);
822 if (!nse)
823 return NULL;
824 }
825
826 return gprs_ns2_ip_connect(bind, remote, nse, nsvci);
827}
828
Harald Welte5bef2cc2020-09-18 22:33:24 +0200829/*! Create, connect and activate a new IP-SNS NSE.
830 * \param[in] bind bind in which the new NS-VC is to be created
831 * \param[in] remote remote address to which to connect
832 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
833 * \return 0 on success; negative on error */
Alexander Couzens6a161492020-07-12 13:45:50 +0200834int gprs_ns2_ip_connect_sns(struct gprs_ns2_vc_bind *bind,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700835 const struct osmo_sockaddr *remote,
Alexander Couzens6a161492020-07-12 13:45:50 +0200836 uint16_t nsei)
837{
838 struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
839 struct gprs_ns2_vc *nsvc;
840
841 if (!nse) {
842 nse = gprs_ns2_create_nse(bind->nsi, nsei);
843 if (!nse)
844 return -1;
845 }
846
847 nsvc = gprs_ns2_ip_bind_connect(bind, nse, remote);
848 if (!nsvc)
849 return -1;
850
851 if (!nse->bss_sns_fi)
852 nse->bss_sns_fi = ns2_sns_bss_fsm_alloc(nse, NULL);
853
854 if (!nse->bss_sns_fi)
855 return -1;
856
857 return ns2_sns_bss_fsm_start(nse, nsvc, remote);
858}
859
Harald Welte5bef2cc2020-09-18 22:33:24 +0200860/*! Find NS-VC for given socket address.
861 * \param[in] nse NS Entity in which to search
862 * \param[in] sockaddr socket address to search for
863 * \return NS-VC matching sockaddr; NULL if none found */
Alexander Couzens38b19e82020-09-23 23:56:37 +0200864struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_nse(struct gprs_ns2_nse *nse,
Vadim Yanitskiya07f25e2020-10-09 21:47:01 +0700865 const struct osmo_sockaddr *sockaddr)
Alexander Couzens6a161492020-07-12 13:45:50 +0200866{
867 struct gprs_ns2_vc *nsvc;
Alexander Couzens9a4cf272020-10-11 20:48:04 +0200868 const struct osmo_sockaddr *remote;
Alexander Couzens6a161492020-07-12 13:45:50 +0200869
870 OSMO_ASSERT(nse);
871 OSMO_ASSERT(sockaddr);
872
873 llist_for_each_entry(nsvc, &nse->nsvc, list) {
Alexander Couzensc4229a42020-10-11 20:58:04 +0200874 remote = gprs_ns2_ip_vc_remote(nsvc);
Alexander Couzens6a161492020-07-12 13:45:50 +0200875 if (!osmo_sockaddr_cmp(sockaddr, remote))
876 return nsvc;
877 }
878
879 return NULL;
880}
881
Alexander Couzens6cb5d5f2020-10-11 23:23:31 +0200882/*!
883 * Iterate over all nsvc of a NS Entity and call the callback.
884 * If the callback returns < 0 it aborts the loop and returns the callback return code.
885 * \param[in] nse NS Entity to iterate over all nsvcs
886 * \param[in] cb the callback to call
887 * \param[inout] cb_data the private data of the callback
888 * \return 0 if the loop completes. If a callback returns < 0 it will returns this value.
889 */
890int gprs_ns2_nse_foreach_nsvc(struct gprs_ns2_nse *nse, gprs_ns2_foreach_nsvc_cb cb, void *cb_data)
891{
892 struct gprs_ns2_vc *nsvc, *tmp;
893 int rc = 0;
894 llist_for_each_entry_safe(nsvc, tmp, &nse->nsvc, list) {
895 rc = cb(nsvc, cb_data);
896 if (rc < 0)
897 return rc;
898 }
899
900 return 0;
901}
902
903
Alexander Couzens6a161492020-07-12 13:45:50 +0200904
Harald Welte5bef2cc2020-09-18 22:33:24 +0200905/*! Bottom-side entry-point for received NS PDU from the driver/bind
Harald Welte5bef2cc2020-09-18 22:33:24 +0200906 * \param[in] nsvc NS-VC for which the message was received
907 * \param msg the received message. Ownership is trasnferred, caller must not free it!
908 * \return 0 on success; negative on error */
Alexander Couzensffd49d02020-09-24 00:47:17 +0200909int ns2_recv_vc(struct gprs_ns2_vc *nsvc,
Alexander Couzens6a161492020-07-12 13:45:50 +0200910 struct msgb *msg)
911{
912 struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
913 struct tlv_parsed tp;
914 int rc = 0;
915
916 if (msg->len < sizeof(struct gprs_ns_hdr))
917 return -EINVAL;
918
919 switch (nsh->pdu_type) {
920 case SNS_PDUT_CONFIG:
921 /* one additional byte ('end flag') before the TLV part starts */
922 rc = ns2_tlv_parse(&tp, nsh->data+1,
923 msgb_l2len(msg) - sizeof(*nsh)-1, 0, 0);
924 if (rc < 0) {
Alexander Couzensbb0a53b2020-10-12 04:18:03 +0200925 LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
Alexander Couzens6a161492020-07-12 13:45:50 +0200926 return rc;
927 }
928 /* All sub-network service related message types */
929 rc = gprs_ns2_sns_rx(nsvc, msg, &tp);
930 break;
931 case SNS_PDUT_ACK:
932 case SNS_PDUT_ADD:
933 case SNS_PDUT_CHANGE_WEIGHT:
934 case SNS_PDUT_DELETE:
935 /* weird layout: NSEI TLV, then value-only transaction IE, then TLV again */
Harald Welte36be9d82020-10-09 15:39:25 +0200936 rc = ns2_tlv_parse(&tp, nsh->data+5,
937 msgb_l2len(msg) - sizeof(*nsh)-5, 0, 0);
Alexander Couzens6a161492020-07-12 13:45:50 +0200938 if (rc < 0) {
Alexander Couzensbb0a53b2020-10-12 04:18:03 +0200939 LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
Alexander Couzens6a161492020-07-12 13:45:50 +0200940 return rc;
941 }
942 tp.lv[NS_IE_NSEI].val = nsh->data+2;
943 tp.lv[NS_IE_NSEI].len = 2;
944 tp.lv[NS_IE_TRANS_ID].val = nsh->data+4;
945 tp.lv[NS_IE_TRANS_ID].len = 1;
946 rc = gprs_ns2_sns_rx(nsvc, msg, &tp);
947 break;
948 case SNS_PDUT_CONFIG_ACK:
949 case SNS_PDUT_SIZE:
950 case SNS_PDUT_SIZE_ACK:
951 rc = ns2_tlv_parse(&tp, nsh->data,
952 msgb_l2len(msg) - sizeof(*nsh), 0, 0);
953 if (rc < 0) {
Alexander Couzensbb0a53b2020-10-12 04:18:03 +0200954 LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
Alexander Couzens6a161492020-07-12 13:45:50 +0200955 return rc;
956 }
957 /* All sub-network service related message types */
958 rc = gprs_ns2_sns_rx(nsvc, msg, &tp);
959 break;
960
961 case NS_PDUT_UNITDATA:
962 rc = gprs_ns2_vc_rx(nsvc, msg, NULL);
963 break;
964 default:
965 rc = ns2_tlv_parse(&tp, nsh->data,
966 msgb_l2len(msg) - sizeof(*nsh), 0, 0);
967 if (rc < 0) {
Alexander Couzensbb0a53b2020-10-12 04:18:03 +0200968 LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse\n");
Alexander Couzens6a161492020-07-12 13:45:50 +0200969 if (nsh->pdu_type != NS_PDUT_STATUS)
970 ns2_tx_status(nsvc, NS_CAUSE_PROTO_ERR_UNSPEC, 0, msg);
971 return rc;
972 }
973 rc = gprs_ns2_vc_rx(nsvc, msg, &tp);
974 break;
975 }
976
977 return rc;
978}
979
Harald Welte5bef2cc2020-09-18 22:33:24 +0200980/*! Notify a nse about the change of a NS-VC.
981 * \param[in] nsvc NS-VC which has detected the change (and shall not be notified).
982 * \param[in] unblocked whether the NSE should be marked as unblocked (true) or blocked (false) */
Alexander Couzens6a161492020-07-12 13:45:50 +0200983void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked)
984{
985 struct gprs_ns2_nse *nse = nsvc->nse;
986 struct gprs_ns2_vc *tmp;
987
988 if (unblocked == nse->alive)
989 return;
990
991 if (unblocked) {
992 /* this is the first unblocked NSVC on an unavailable NSE */
993 nse->alive = true;
Daniel Willmann15c09a82020-11-03 23:05:43 +0100994 ns2_prim_status_ind(nse, NULL, 0, NS_AFF_CAUSE_RECOVERY);
Alexander Couzensda0a2852020-10-01 23:24:07 +0200995 nse->first = false;
Alexander Couzens6a161492020-07-12 13:45:50 +0200996 return;
997 }
998
999 /* check if there are any remaining alive vcs */
1000 llist_for_each_entry(tmp, &nse->nsvc, list) {
1001 if (tmp == nsvc)
1002 continue;
1003
1004 if (gprs_ns2_vc_is_unblocked(tmp)) {
1005 /* there is at least one remaining alive NSVC */
1006 return;
1007 }
1008 }
1009
1010 /* nse became unavailable */
1011 nse->alive = false;
Daniel Willmann15c09a82020-11-03 23:05:43 +01001012 ns2_prim_status_ind(nse, NULL, 0, NS_AFF_CAUSE_FAILURE);
Alexander Couzens6a161492020-07-12 13:45:50 +02001013}
1014
1015/*! Create a new GPRS NS instance
Harald Welte5bef2cc2020-09-18 22:33:24 +02001016 * \param[in] ctx a talloc context to allocate NS instance from
Alexander Couzenscce88282020-10-26 00:25:50 +01001017 * \param[in] cb Call-back function for dispatching primitives to the user. The Call-back must free all msgb* given in the primitive.
Harald Welte5bef2cc2020-09-18 22:33:24 +02001018 * \param[in] cb_data transparent user data passed to Call-back
1019 * \returns dynamically allocated gprs_ns_inst; NULL on error */
Alexander Couzens6a161492020-07-12 13:45:50 +02001020struct gprs_ns2_inst *gprs_ns2_instantiate(void *ctx, osmo_prim_cb cb, void *cb_data)
1021{
1022 struct gprs_ns2_inst *nsi;
1023
1024 nsi = talloc_zero(ctx, struct gprs_ns2_inst);
1025 if (!nsi)
1026 return NULL;
1027
1028 nsi->cb = cb;
1029 nsi->cb_data = cb_data;
1030 INIT_LLIST_HEAD(&nsi->binding);
1031 INIT_LLIST_HEAD(&nsi->nse);
1032
1033 nsi->timeout[NS_TOUT_TNS_BLOCK] = 3;
1034 nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES] = 3;
1035 nsi->timeout[NS_TOUT_TNS_RESET] = 3;
1036 nsi->timeout[NS_TOUT_TNS_RESET_RETRIES] = 3;
1037 nsi->timeout[NS_TOUT_TNS_TEST] = 30;
1038 nsi->timeout[NS_TOUT_TNS_ALIVE] = 3;
1039 nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES] = 10;
1040 nsi->timeout[NS_TOUT_TSNS_PROV] = 3; /* 1..10 */
1041
1042 return nsi;
1043}
1044
Harald Welte5bef2cc2020-09-18 22:33:24 +02001045/*! Destroy a NS Instance (including all its NSEs, binds, ...).
1046 * \param[in] nsi NS instance to destroy */
Alexander Couzens6a161492020-07-12 13:45:50 +02001047void gprs_ns2_free(struct gprs_ns2_inst *nsi)
1048{
Alexander Couzens6a161492020-07-12 13:45:50 +02001049 if (!nsi)
1050 return;
1051
Alexander Couzens4b6c8af2020-10-11 20:15:25 +02001052 gprs_ns2_free_nses(nsi);
Alexander Couzens896fcd52020-10-11 19:52:36 +02001053 gprs_ns2_free_binds(nsi);
Alexander Couzens35315042020-10-10 03:28:17 +02001054
1055 talloc_free(nsi);
Alexander Couzens6a161492020-07-12 13:45:50 +02001056}
1057
Harald Welte5bef2cc2020-09-18 22:33:24 +02001058/*! Configure whether a NS Instance should dynamically create NSEs based on incoming traffic.
1059 * \param nsi the instance to modify
1060 * \param create_nse if NSE can be created on receiving package. SGSN set this.
1061 * \return 0 on success; negative on error
Alexander Couzens6a161492020-07-12 13:45:50 +02001062 */
1063int gprs_ns2_dynamic_create_nse(struct gprs_ns2_inst *nsi, bool create_nse)
1064{
1065 nsi->create_nse = create_nse;
1066
1067 return 0;
1068}
1069
Harald Welte5bef2cc2020-09-18 22:33:24 +02001070/*! Start the NS-ALIVE FSM in all NS-VCs of given NSE.
1071 * \param[in] nse NS Entity in whihc to start NS-ALIVE FSMs */
Alexander Couzens6a161492020-07-12 13:45:50 +02001072void gprs_ns2_start_alive_all_nsvcs(struct gprs_ns2_nse *nse)
1073{
1074 struct gprs_ns2_vc *nsvc;
1075 OSMO_ASSERT(nse);
1076
1077 llist_for_each_entry(nsvc, &nse->nsvc, list) {
1078 if (nsvc->sns_only)
1079 continue;
1080
1081 gprs_ns2_vc_fsm_start(nsvc);
1082 }
1083}
1084
Harald Welte5bef2cc2020-09-18 22:33:24 +02001085/*! Set the mode of given bind.
1086 * \param[in] bind the bind we want to set the mode of
1087 * \param[in] modde mode to set bind to */
Alexander Couzens6a161492020-07-12 13:45:50 +02001088void gprs_ns2_bind_set_mode(struct gprs_ns2_vc_bind *bind, enum gprs_ns2_vc_mode mode)
1089{
1090 bind->vc_mode = mode;
1091}
1092
Harald Welte5bef2cc2020-09-18 22:33:24 +02001093/*! Destroy a given bind.
1094 * \param[in] bind the bind we want to destroy */
Alexander Couzens6a161492020-07-12 13:45:50 +02001095void gprs_ns2_free_bind(struct gprs_ns2_vc_bind *bind)
1096{
1097 struct gprs_ns2_vc *nsvc, *tmp;
1098 if (!bind)
1099 return;
1100
1101 llist_for_each_entry_safe(nsvc, tmp, &bind->nsvc, blist) {
1102 gprs_ns2_free_nsvc(nsvc);
1103 }
1104
1105 if (bind->driver->free_bind)
1106 bind->driver->free_bind(bind);
1107
1108 llist_del(&bind->list);
1109 talloc_free(bind);
1110}
Alexander Couzens896fcd52020-10-11 19:52:36 +02001111
1112void gprs_ns2_free_binds(struct gprs_ns2_inst *nsi)
1113{
1114 struct gprs_ns2_vc_bind *bind, *tbind;
1115
1116 llist_for_each_entry_safe(bind, tbind, &nsi->binding, list) {
1117 gprs_ns2_free_bind(bind);
1118 }
1119}
Alexander Couzens6a161492020-07-12 13:45:50 +02001120/*! @} */