blob: cae03eede7048a3c72f827718826ff4cf2595ae7 [file] [log] [blame]
Alexander Couzens841817e2020-11-19 00:41:29 +01001/*! \file gprs_ns2_fr.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
Harald Welted06128d2021-01-30 21:17:21 +01007/* (C) 2009-2021 by Harald Welte <laforge@gnumonks.org>
Alexander Couzens841817e2020-11-19 00:41:29 +01008 * (C) 2020 sysmocom - s.f.m.c. GmbH
9 * Author: Alexander Couzens <lynxis@fe80.eu>
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#include <errno.h>
31#include <string.h>
32#include <unistd.h>
33
34#include <sys/socket.h>
35#include <netinet/in.h>
36#include <netinet/ip.h>
37#include <netinet/ip6.h>
38#include <arpa/inet.h>
Alexander Couzens5c96f5d2020-12-17 04:45:03 +010039#include <linux/if.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010040
41#include <sys/ioctl.h>
42#include <netpacket/packet.h>
43#include <linux/if_ether.h>
44#include <linux/hdlc.h>
Alexander Couzens5c96f5d2020-12-17 04:45:03 +010045#include <linux/hdlc/ioctl.h>
46#include <linux/sockios.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010047
48#include <osmocom/gprs/frame_relay.h>
49#include <osmocom/core/byteswap.h>
Harald Welte76346072021-01-31 11:54:02 +010050#include <osmocom/core/stat_item.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010051#include <osmocom/core/logging.h>
52#include <osmocom/core/msgb.h>
53#include <osmocom/core/select.h>
54#include <osmocom/core/socket.h>
Harald Welted06128d2021-01-30 21:17:21 +010055#include <osmocom/core/timer.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010056#include <osmocom/core/talloc.h>
57#include <osmocom/gprs/gprs_ns2.h>
Harald Welted06128d2021-01-30 21:17:21 +010058#include <osmocom/gprs/protocol/gsm_08_16.h>
59#include <osmocom/gprs/protocol/gsm_08_18.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010060
Pau Espin Pedrold41800c2020-12-07 13:35:24 +010061#ifdef ENABLE_LIBMNL
62#include <osmocom/core/mnl.h>
63#endif
64
Harald Welte56f08a32020-12-01 23:07:32 +010065#include "config.h"
Alexander Couzens841817e2020-11-19 00:41:29 +010066#include "common_vty.h"
67#include "gprs_ns2_internal.h"
68
69#define GRE_PTYPE_FR 0x6559
70#define GRE_PTYPE_IPv4 0x0800
71#define GRE_PTYPE_IPv6 0x86dd
72#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
73
74#ifndef IPPROTO_GRE
75# define IPPROTO_GRE 47
76#endif
77
Harald Welted06128d2021-01-30 21:17:21 +010078#define E1_LINERATE 2048000
79#define E1_SLOTS_TOTAL 32
80#define E1_SLOTS_USED 31
81/* usable bitrate of the E1 superchannel with 31 of 32 timeslots */
82#define SUPERCHANNEL_LINERATE (E1_LINERATE*E1_SLOTS_USED)/E1_SLOTS_TOTAL
83/* nanoseconds per bit (504) */
84#define BIT_DURATION_NS (1000000000 / SUPERCHANNEL_LINERATE)
85
Alexander Couzens841817e2020-11-19 00:41:29 +010086struct gre_hdr {
87 uint16_t flags;
88 uint16_t ptype;
89} __attribute__ ((packed));
90
91static void free_bind(struct gprs_ns2_vc_bind *bind);
92static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg);
93
94struct gprs_ns2_vc_driver vc_driver_fr = {
95 .name = "GB frame relay",
96 .free_bind = free_bind,
97};
98
99struct priv_bind {
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100100 char netif[IFNAMSIZ];
Alexander Couzens841817e2020-11-19 00:41:29 +0100101 struct osmo_fr_link *link;
Harald Welte41b188b2020-12-10 22:00:23 +0100102 int ifindex;
Harald Welte56f08a32020-12-01 23:07:32 +0100103 bool if_running;
Harald Welted06128d2021-01-30 21:17:21 +0100104 /* backlog queue for AF_PACKET / ENOBUFS handling (see OS#4993) */
105 struct {
106 /* file-descriptor for AF_PACKET socket */
107 struct osmo_fd ofd;
Harald Welte93740612021-02-02 19:54:10 +0100108 /* LMI bucket (we only store the last LMI message, no need to queue */
109 struct msgb *lmi_msg;
110 /* list of NS msgb (backlog) */
Harald Welted06128d2021-01-30 21:17:21 +0100111 struct llist_head list;
112 /* timer to trigger next attempt of AF_PACKET write */
113 struct osmo_timer_list timer;
114 /* re-try after that many micro-seconds */
115 uint32_t retry_us;
116 } backlog;
Alexander Couzens841817e2020-11-19 00:41:29 +0100117};
118
119struct priv_vc {
120 struct osmo_sockaddr remote;
121 uint16_t dlci;
122 struct osmo_fr_dlc *dlc;
123};
124
125static void free_vc(struct gprs_ns2_vc *nsvc)
126{
Alexander Couzensea377242021-01-17 16:51:55 +0100127 if (!nsvc)
128 return;
Alexander Couzens841817e2020-11-19 00:41:29 +0100129
130 if (!nsvc->priv)
131 return;
132
Alexander Couzens55bc8692021-01-18 18:39:57 +0100133 OSMO_ASSERT(gprs_ns2_is_fr_bind(nsvc->bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100134 talloc_free(nsvc->priv);
135 nsvc->priv = NULL;
136}
137
138static void dump_vty(const struct gprs_ns2_vc_bind *bind, struct vty *vty, bool _stats)
139{
140 struct priv_bind *priv;
141 struct gprs_ns2_vc *nsvc;
Harald Welte48bd76c2020-12-01 16:53:06 +0100142 struct osmo_fr_link *fr_link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100143
144 if (!bind)
145 return;
146
147 priv = bind->priv;
Harald Welte48bd76c2020-12-01 16:53:06 +0100148 fr_link = priv->link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100149
Harald Welte56f08a32020-12-01 23:07:32 +0100150 vty_out(vty, "FR bind: %s, role: %s, link: %s%s", priv->netif,
151 osmo_fr_role_str(fr_link->role), priv->if_running ? "UP" : "DOWN", VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100152
153 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
Harald Welte96ec84a2020-12-01 17:56:05 +0100154 vty_out(vty, " NSVCI %05u: %s%s", nsvc->nsvci, gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100155 }
156
157 priv = bind->priv;
158}
159
160/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
161static void free_bind(struct gprs_ns2_vc_bind *bind)
162{
163 struct priv_bind *priv;
Harald Welted06128d2021-01-30 21:17:21 +0100164 struct msgb *msg, *msg2;
Alexander Couzens841817e2020-11-19 00:41:29 +0100165
166 if (!bind)
167 return;
168
Alexander Couzens24d9e802021-02-03 11:30:43 +0100169 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100170 priv = bind->priv;
171
172 OSMO_ASSERT(llist_empty(&bind->nsvc));
173
Harald Welted06128d2021-01-30 21:17:21 +0100174 osmo_timer_del(&priv->backlog.timer);
175 llist_for_each_entry_safe(msg, msg2, &priv->backlog.list, list) {
176 msgb_free(msg);
177 }
Harald Welte93740612021-02-02 19:54:10 +0100178 msgb_free(priv->backlog.lmi_msg);
Harald Welted06128d2021-01-30 21:17:21 +0100179
Alexander Couzens841817e2020-11-19 00:41:29 +0100180 osmo_fr_link_free(priv->link);
Harald Welted06128d2021-01-30 21:17:21 +0100181 osmo_fd_close(&priv->backlog.ofd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100182 talloc_free(priv);
183}
184
Harald Welte9d28ce52021-01-31 18:38:20 +0100185static void fr_dlci_status_cb(struct osmo_fr_dlc *dlc, void *cb_data, bool active)
186{
187 struct gprs_ns2_vc *nsvc = cb_data;
188
189 if (active) {
190 ns2_vc_fsm_start(nsvc);
191 } else {
192 ns2_vc_force_unconfigured(nsvc);
193 }
194}
195
Alexander Couzens841817e2020-11-19 00:41:29 +0100196static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,
197 struct gprs_ns2_vc *nsvc,
198 uint16_t dlci)
199{
200 struct priv_bind *privb = bind->priv;
201 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
202 if (!priv)
203 return NULL;
204
Alexander Couzens55bc8692021-01-18 18:39:57 +0100205 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100206 nsvc->priv = priv;
207 priv->dlci = dlci;
208 priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);
209 if (!priv->dlc) {
210 nsvc->priv = NULL;
211 talloc_free(priv);
212 return NULL;
213 }
214
Harald Welte2cc1d4d2021-01-31 18:33:31 +0100215 priv->dlc->cb_data = nsvc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100216 priv->dlc->rx_cb = fr_dlci_rx_cb;
Harald Welte9d28ce52021-01-31 18:38:20 +0100217 priv->dlc->status_cb = fr_dlci_status_cb;
Alexander Couzens841817e2020-11-19 00:41:29 +0100218
219 return priv;
220}
221
222int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
223 uint16_t dlci,
224 struct gprs_ns2_vc **result)
225{
226 struct gprs_ns2_vc *nsvc;
227 struct priv_vc *vcpriv;
228
Alexander Couzens55bc8692021-01-18 18:39:57 +0100229 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100230 if (!result)
231 return -EINVAL;
232
233 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
234 vcpriv = nsvc->priv;
235 if (vcpriv->dlci != dlci) {
236 *result = nsvc;
237 return 0;
238 }
239 }
240
241 return 1;
242}
243
244/* PDU from the network interface towards the fr layer (upwards) */
Harald Welted06128d2021-01-30 21:17:21 +0100245static int fr_netif_ofd_cb(struct osmo_fd *bfd, uint32_t what)
Alexander Couzens841817e2020-11-19 00:41:29 +0100246{
247 struct gprs_ns2_vc_bind *bind = bfd->data;
248 struct priv_bind *priv = bind->priv;
Harald Welted06128d2021-01-30 21:17:21 +0100249 struct msgb *msg;
Harald Welte41b188b2020-12-10 22:00:23 +0100250 struct sockaddr_ll sll;
251 socklen_t sll_len = sizeof(sll);
Alexander Couzens841817e2020-11-19 00:41:29 +0100252 int rc = 0;
253
Harald Welted06128d2021-01-30 21:17:21 +0100254 /* we only handle read here. write to AF_PACKET sockets cannot be triggered
255 * by select or poll, see OS#4995 */
256 if (!(what & OSMO_FD_READ))
257 return 0;
258
259 msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
Alexander Couzens841817e2020-11-19 00:41:29 +0100260 if (!msg)
261 return -ENOMEM;
262
Harald Welte41b188b2020-12-10 22:00:23 +0100263 rc = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)&sll, &sll_len);
Alexander Couzens841817e2020-11-19 00:41:29 +0100264 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100265 LOGBIND(bind, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n", strerror(errno));
Alexander Couzens841817e2020-11-19 00:41:29 +0100266 goto out_err;
267 } else if (rc == 0) {
268 goto out_err;
269 }
270
Harald Welte41b188b2020-12-10 22:00:23 +0100271 /* ignore any packets that we might have received for a different interface, between
272 * the socket() and the bind() call */
273 if (sll.sll_ifindex != priv->ifindex)
274 goto out_err;
275
Alexander Couzens841817e2020-11-19 00:41:29 +0100276 msgb_put(msg, rc);
277 msg->dst = priv->link;
278 return osmo_fr_rx(msg);
279
280out_err:
281 msgb_free(msg);
282 return rc;
283}
284
285/* PDU from the frame relay towards the NS-VC (upwards) */
286static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)
287{
288 int rc;
289 struct gprs_ns2_vc *nsvc = cb_data;
290
291 rc = ns2_recv_vc(nsvc, msg);
292
293 return rc;
294}
295
Harald Welted06128d2021-01-30 21:17:21 +0100296static int fr_netif_write_one(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
Alexander Couzens841817e2020-11-19 00:41:29 +0100297{
Harald Welted06128d2021-01-30 21:17:21 +0100298 struct priv_bind *priv = bind->priv;
299 unsigned int len = msgb_length(msg);
Harald Weltef0073d72021-01-30 11:41:13 +0100300 int rc;
301
Harald Welted06128d2021-01-30 21:17:21 +0100302 /* estimate the retry time based on the data rate it takes to transmit */
303 priv->backlog.retry_us = (BIT_DURATION_NS * 8 * len) / 1000;
304
305 rc = write(priv->backlog.ofd.fd, msgb_data(msg), len);
306 if (rc == len) {
307 msgb_free(msg);
308 return 0;
309 } else if (rc < 0) {
310 /* don't free, the caller might want to re-transmit */
311 switch (errno) {
312 case EAGAIN:
313 case ENOBUFS:
314 return -errno;
315 default:
316 LOGBIND(bind, LOGL_ERROR, "error during write to AF_PACKET: %s\n", strerror(errno));
317 return -errno;
318 }
319 } else {
320 /* short write */
321 LOGBIND(bind, LOGL_ERROR, "short write on AF_PACKET: %d < %d\n", rc, len);
322 msgb_free(msg);
323 return 0;
324 }
Alexander Couzens841817e2020-11-19 00:41:29 +0100325}
326
327/*! determine if given bind is for FR-GRE encapsulation. */
328int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
329{
330 return (bind->driver == &vc_driver_fr);
331}
332
333/* PDU from the NS-VC towards the frame relay layer (downwards) */
334static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
335{
336 struct priv_vc *vcpriv = nsvc->priv;
337
338 msg->dst = vcpriv->dlc;
339 return osmo_fr_tx_dlc(msg);
340}
341
Harald Welted06128d2021-01-30 21:17:21 +0100342static void enqueue_at_head(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
343{
344 struct priv_bind *priv = bind->priv;
345 llist_add(&msg->list, &priv->backlog.list);
Harald Welte76346072021-01-31 11:54:02 +0100346 osmo_stat_item_inc(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100347 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
348}
349
350static void enqueue_at_tail(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
351{
352 struct priv_bind *priv = bind->priv;
353 llist_add_tail(&msg->list, &priv->backlog.list);
Harald Welte76346072021-01-31 11:54:02 +0100354 osmo_stat_item_inc(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100355 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
356}
357
358#define LMI_Q933A_DLCI 0
359
360/* enqueue to backlog (LMI, signaling) or drop (userdata msg) */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100361static int backlog_enqueue_or_free(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
Harald Welted06128d2021-01-30 21:17:21 +0100362{
Harald Welte93740612021-02-02 19:54:10 +0100363 struct priv_bind *priv = bind->priv;
Harald Welted06128d2021-01-30 21:17:21 +0100364 uint8_t dlci = msg->data[0];
365 uint8_t ns_pdu_type;
366 uint16_t bvci;
367
368 if (msgb_length(msg) < 1)
369 goto out_free;
370
371 /* we want to enqueue only Q.933 LMI traffic or NS signaling; NOT user traffic */
372 switch (dlci) {
373 case LMI_Q933A_DLCI:
Harald Welte93740612021-02-02 19:54:10 +0100374 /* always store only the last LMI message in the lmi_msg bucket */
375 msgb_free(priv->backlog.lmi_msg);
376 priv->backlog.lmi_msg = msg;
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100377 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100378 default:
379 if (msgb_length(msg) < 3)
380 break;
381 ns_pdu_type = msg->data[2];
382 switch (ns_pdu_type) {
383 case NS_PDUT_UNITDATA:
384 if (msgb_length(msg) < 6)
385 break;
386 bvci = osmo_load16be(msg->data + 4);
387 /* enqueue BVCI=0 traffic at tail of queue */
388 if (bvci == BVCI_SIGNALLING) {
389 enqueue_at_tail(bind, msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100390 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100391 }
392 break;
393 default:
394 /* enqueue NS signaling traffic at head of queue */
395 enqueue_at_head(bind, msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100396 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100397 }
398 break;
399 }
400
401out_free:
402 /* drop everything that is not LMI, NS-signaling or BVCI-0 */
403 msgb_free(msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100404 return -1;
Harald Welted06128d2021-01-30 21:17:21 +0100405}
406
407static void fr_backlog_timer_cb(void *data)
408{
409 struct gprs_ns2_vc_bind *bind = data;
410 struct priv_bind *priv = bind->priv;
411 int i, rc;
412
Harald Welte93740612021-02-02 19:54:10 +0100413 /* first try to get rid of the LMI message, if any */
414 if (priv->backlog.lmi_msg) {
415 rc = fr_netif_write_one(bind, priv->backlog.lmi_msg);
416 if (rc < 0)
417 goto restart_timer;
418 /* fr_netif_write_one() has just free'd it */
419 priv->backlog.lmi_msg = NULL;
420 }
421
Harald Welted06128d2021-01-30 21:17:21 +0100422 /* attempt to send up to 10 messages in every timer */
423 for (i = 0; i < 10; i++) {
424 struct msgb *msg = msgb_dequeue(&priv->backlog.list);
425 if (!msg)
426 break;
427
428 rc = fr_netif_write_one(bind, msg);
429 if (rc < 0) {
430 /* re-add at head of list */
431 llist_add(&msg->list, &priv->backlog.list);
432 break;
433 }
Harald Welte76346072021-01-31 11:54:02 +0100434 osmo_stat_item_dec(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100435 }
436
Harald Welte93740612021-02-02 19:54:10 +0100437restart_timer:
Harald Welted06128d2021-01-30 21:17:21 +0100438 /* re-start timer if we still have data in the queue */
439 if (!llist_empty(&priv->backlog.list))
440 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
441}
442
Alexander Couzens841817e2020-11-19 00:41:29 +0100443/* PDU from the frame relay layer towards the network interface (downwards) */
444int fr_tx_cb(void *data, struct msgb *msg)
445{
446 struct gprs_ns2_vc_bind *bind = data;
447 struct priv_bind *priv = bind->priv;
Harald Welted06128d2021-01-30 21:17:21 +0100448 int rc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100449
Harald Welted06128d2021-01-30 21:17:21 +0100450 if (llist_empty(&priv->backlog.list)) {
451 /* attempt to transmit right now */
452 rc = fr_netif_write_one(bind, msg);
453 if (rc < 0) {
454 /* enqueue to backlog in case it fails */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100455 return backlog_enqueue_or_free(bind, msg);
Harald Welted06128d2021-01-30 21:17:21 +0100456 }
457 } else {
458 /* enqueue to backlog */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100459 return backlog_enqueue_or_free(bind, msg);
Alexander Couzens60021a42020-12-17 02:48:22 +0100460 }
Alexander Couzens841817e2020-11-19 00:41:29 +0100461
Alexander Couzens60021a42020-12-17 02:48:22 +0100462 return 0;
Alexander Couzens841817e2020-11-19 00:41:29 +0100463}
464
465static int devname2ifindex(const char *ifname)
466{
467 struct ifreq ifr;
468 int sk, rc;
469
470 sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
471 if (sk < 0)
472 return sk;
473
474
475 memset(&ifr, 0, sizeof(ifr));
Neels Hofmeyr475a0ac2020-12-17 18:10:34 +0100476 OSMO_STRLCPY_ARRAY(ifr.ifr_name, ifname);
Alexander Couzens841817e2020-11-19 00:41:29 +0100477
478 rc = ioctl(sk, SIOCGIFINDEX, &ifr);
479 close(sk);
480 if (rc < 0)
481 return rc;
482
483 return ifr.ifr_ifindex;
484}
485
Harald Weltef2949742021-01-20 14:54:14 +0100486static int open_socket(int ifindex, const struct gprs_ns2_vc_bind *nsbind)
Alexander Couzens841817e2020-11-19 00:41:29 +0100487{
488 struct sockaddr_ll addr;
Harald Welte4ed0f4e2020-12-10 21:50:32 +0100489 int fd, rc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100490
Alexander Couzens841817e2020-11-19 00:41:29 +0100491 memset(&addr, 0, sizeof(addr));
492 addr.sll_family = AF_PACKET;
493 addr.sll_protocol = htons(ETH_P_ALL);
494 addr.sll_ifindex = ifindex;
495
Harald Welte5bea72e2020-12-10 22:06:21 +0100496 fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_HDLC));
Alexander Couzens841817e2020-11-19 00:41:29 +0100497 if (fd < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100498 LOGBIND(nsbind, LOGL_ERROR, "Can not create AF_PACKET socket. Are you root or have CAP_NET_RAW?\n");
Alexander Couzens841817e2020-11-19 00:41:29 +0100499 return fd;
500 }
501
Harald Welte41b188b2020-12-10 22:00:23 +0100502 /* there's a race condition between the above syscall and the bind() call below,
503 * causing other packets to be received in between */
504
Alexander Couzens841817e2020-11-19 00:41:29 +0100505 rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
506 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100507 LOGBIND(nsbind, LOGL_ERROR, "Can not bind AF_PACKET socket to ifindex %d\n", ifindex);
Alexander Couzens841817e2020-11-19 00:41:29 +0100508 close(fd);
509 return rc;
510 }
511
512 return fd;
513}
514
Harald Welte56f08a32020-12-01 23:07:32 +0100515#ifdef ENABLE_LIBMNL
516
517#include <osmocom/core/mnl.h>
Harald Welte56f08a32020-12-01 23:07:32 +0100518#include <linux/if_link.h>
519#include <linux/rtnetlink.h>
520
521#ifndef ARPHRD_FRAD
522#define ARPHRD_FRAD 770
523#endif
524
525/* validate the netlink attributes */
526static int data_attr_cb(const struct nlattr *attr, void *data)
527{
528 const struct nlattr **tb = data;
529 int type = mnl_attr_get_type(attr);
530
531 if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
532 return MNL_CB_OK;
533
534 switch (type) {
535 case IFLA_MTU:
536 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
537 return MNL_CB_ERROR;
538 break;
539 case IFLA_IFNAME:
540 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
541 return MNL_CB_ERROR;
542 break;
543 }
544 tb[type] = attr;
545 return MNL_CB_OK;
546}
547
548/* find the bind for the netdev (if any) */
549static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname)
550{
551 struct gprs_ns2_vc_bind *bind;
552
553 llist_for_each_entry(bind, &nsi->binding, list) {
554 struct priv_bind *bpriv = bind->priv;
555 if (!strcmp(bpriv->netif, ifname))
556 return bind;
557 }
558
559 return NULL;
560}
561
562/* handle a single netlink message received via libmnl */
563static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data)
564{
565 struct osmo_mnl *omnl = data;
566 struct gprs_ns2_vc_bind *bind;
567 struct nlattr *tb[IFLA_MAX+1] = {};
568 struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
569 struct gprs_ns2_inst *nsi;
570 const char *ifname;
571 bool if_running;
572
573 OSMO_ASSERT(omnl);
574 OSMO_ASSERT(ifm);
575
576 nsi = omnl->priv;
577
578 if (ifm->ifi_type != ARPHRD_FRAD)
579 return MNL_CB_OK;
580
581 mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
582
583 if (!tb[IFLA_IFNAME])
584 return MNL_CB_OK;
585 ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
586 if_running = !!(ifm->ifi_flags & IFF_RUNNING);
587
588 bind = bind4netdev(nsi, ifname);
589 if (bind) {
590 struct priv_bind *bpriv = bind->priv;
591 if (bpriv->if_running != if_running) {
592 /* update running state */
Harald Weltef2949742021-01-20 14:54:14 +0100593 LOGBIND(bind, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
594 ifname, if_running ? "UP" : "DOWN");
Harald Welte56f08a32020-12-01 23:07:32 +0100595 bpriv->if_running = if_running;
596 }
597 }
598
599 return MNL_CB_OK;
600}
601
602/* trigger one initial dump of all link information */
603static void linkmon_initial_dump(struct osmo_mnl *omnl)
604{
605 char buf[MNL_SOCKET_BUFFER_SIZE];
606 struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
607 struct rtgenmsg *rt;
608
609 nlh->nlmsg_type = RTM_GETLINK;
610 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
611 nlh->nlmsg_seq = time(NULL);
612 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
613 rt->rtgen_family = AF_PACKET;
614
615 if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
616 LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno));
617 }
618
619 /* the response[s] will be handled just like the events */
620}
621#endif /* LIBMNL */
622
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100623static int set_ifupdown(const char *netif, bool up)
624{
625 int sock, rc;
626 struct ifreq req;
627
628 sock = socket(AF_INET, SOCK_DGRAM, 0);
629 if (sock < 0)
630 return sock;
631
632 memset(&req, 0, sizeof req);
Harald Welte7f01b682020-12-21 12:39:38 +0100633 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100634
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100635 rc = ioctl(sock, SIOCGIFFLAGS, &req);
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100636 if (rc < 0) {
637 close(sock);
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100638 return rc;
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100639 }
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100640
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100641 if ((req.ifr_flags & IFF_UP) == up) {
642 close(sock);
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100643 return 0;
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100644 }
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100645
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100646 if (up)
647 req.ifr_flags |= IFF_UP;
648
649 rc = ioctl(sock, SIOCSIFFLAGS, &req);
650 close(sock);
651 return rc;
652}
653
Harald Weltef2949742021-01-20 14:54:14 +0100654static int setup_device(const char *netif, const struct gprs_ns2_vc_bind *bind)
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100655{
656 int sock, rc;
657 char buffer[128];
658 fr_proto *fr = (void*)buffer;
659 struct ifreq req;
660
661 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
662 if (sock < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100663 LOGBIND(bind, LOGL_ERROR, "%s: Unable to create socket: %s\n",
664 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100665 return sock;
666 }
667
668 memset(&req, 0, sizeof(struct ifreq));
669 memset(&buffer, 0, sizeof(buffer));
Harald Welteb8de1882020-12-21 12:40:45 +0100670 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100671 req.ifr_settings.ifs_ifsu.sync = (void*)buffer;
672 req.ifr_settings.size = sizeof(buffer);
673 req.ifr_settings.type = IF_GET_PROTO;
674
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100675 /* EINVAL is returned when no protocol has been set */
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100676 rc = ioctl(sock, SIOCWANDEV, &req);
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100677 if (rc < 0 && errno != EINVAL) {
Harald Weltef2949742021-01-20 14:54:14 +0100678 LOGBIND(bind, LOGL_ERROR, "%s: Unable to get FR protocol information: %s\n",
679 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100680 goto err;
681 }
682
683 /* check if the device is good */
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100684 if (rc == 0 && req.ifr_settings.type == IF_PROTO_FR && fr->lmi == LMI_NONE) {
Harald Weltef2949742021-01-20 14:54:14 +0100685 LOGBIND(bind, LOGL_NOTICE, "%s: has correct frame relay mode and lmi\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100686 goto ifup;
687 }
688
689 /* modify the device to match */
690 rc = set_ifupdown(netif, false);
691 if (rc) {
Harald Weltef2949742021-01-20 14:54:14 +0100692 LOGBIND(bind, LOGL_ERROR, "Unable to bring down the device %s: %s\n",
693 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100694 goto err;
695 }
696
697 memset(&req, 0, sizeof(struct ifreq));
698 memset(fr, 0, sizeof(fr_proto));
Harald Welteb8de1882020-12-21 12:40:45 +0100699 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100700 req.ifr_settings.type = IF_PROTO_FR;
701 req.ifr_settings.size = sizeof(fr_proto);
702 req.ifr_settings.ifs_ifsu.fr = fr;
703 fr->lmi = LMI_NONE;
704 /* even those settings aren't used, they must be in the range */
705 /* polling verification timer*/
706 fr->t391 = 10;
707 /* link integrity verification polling timer */
708 fr->t392 = 15;
709 /* full status polling counter*/
710 fr->n391 = 6;
711 /* error threshold */
712 fr->n392 = 3;
713 /* monitored events count */
714 fr->n393 = 4;
715
Harald Weltef2949742021-01-20 14:54:14 +0100716 LOGBIND(bind, LOGL_INFO, "%s: Setting frame relay related parameters\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100717 rc = ioctl(sock, SIOCWANDEV, &req);
718 if (rc) {
Harald Weltef2949742021-01-20 14:54:14 +0100719 LOGBIND(bind, LOGL_ERROR, "%s: Unable to set FR protocol on information: %s\n",
720 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100721 goto err;
722 }
723
724ifup:
725 rc = set_ifupdown(netif, true);
726 if (rc)
Harald Weltef2949742021-01-20 14:54:14 +0100727 LOGBIND(bind, LOGL_ERROR, "Unable to bring up the device %s: %s\n",
728 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100729err:
730 close(sock);
731 return rc;
732}
Harald Welte56f08a32020-12-01 23:07:32 +0100733
Alexander Couzens841817e2020-11-19 00:41:29 +0100734/*! Create a new bind for NS over FR.
735 * \param[in] nsi NS instance in which to create the bind
736 * \param[in] netif Network interface to bind to
737 * \param[in] fr_network
738 * \param[in] fr_role
Alexander Couzensc80a8742021-02-03 11:27:52 +0100739 * \param[out] result pointer to the created bind or if a bind with the name exists return the bind.
740 * \return 0 on success; negative on error. -EALREADY returned in case a bind with the name exists */
Alexander Couzens841817e2020-11-19 00:41:29 +0100741int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100742 const char *name,
Alexander Couzens841817e2020-11-19 00:41:29 +0100743 const char *netif,
744 struct osmo_fr_network *fr_network,
745 enum osmo_fr_role fr_role,
746 struct gprs_ns2_vc_bind **result)
747{
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100748 struct gprs_ns2_vc_bind *bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100749 struct priv_bind *priv;
750 struct osmo_fr_link *fr_link;
751 int rc = 0;
752
Harald Weltec3aa8f92021-01-31 11:41:34 +0100753 if (strlen(netif) > IFNAMSIZ)
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100754 return -EINVAL;
755
Alexander Couzensc80a8742021-02-03 11:27:52 +0100756 bind = gprs_ns2_bind_by_name(nsi, name);
757 if (bind) {
758 if (result)
759 *result = bind;
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100760 return -EALREADY;
Alexander Couzensc80a8742021-02-03 11:27:52 +0100761 }
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100762
Harald Weltec3aa8f92021-01-31 11:41:34 +0100763 rc = ns2_bind_alloc(nsi, name, &bind);
764 if (rc < 0)
765 return rc;
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100766
Alexander Couzens841817e2020-11-19 00:41:29 +0100767 bind->driver = &vc_driver_fr;
Alexander Couzensaac90162020-11-19 02:44:04 +0100768 bind->ll = GPRS_NS2_LL_FR;
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100769 /* 2 mbit */
770 bind->transfer_capability = 2;
Alexander Couzens841817e2020-11-19 00:41:29 +0100771 bind->send_vc = fr_vc_sendmsg;
772 bind->free_vc = free_vc;
773 bind->dump_vty = dump_vty;
Alexander Couzens841817e2020-11-19 00:41:29 +0100774 priv = bind->priv = talloc_zero(bind, struct priv_bind);
775 if (!priv) {
Harald Weltebdfb8b92021-01-31 11:44:57 +0100776 rc = -ENOMEM;
Harald Weltec3aa8f92021-01-31 11:41:34 +0100777 goto err_bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100778 }
779
Neels Hofmeyr8fef7612020-12-17 18:19:19 +0100780 OSMO_STRLCPY_ARRAY(priv->netif, netif);
Alexander Couzens841817e2020-11-19 00:41:29 +0100781
Alexander Couzens841817e2020-11-19 00:41:29 +0100782 /* FIXME: move fd handling into socket.c */
783 fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);
784 if (!fr_link) {
785 rc = -EINVAL;
Harald Weltec3aa8f92021-01-31 11:41:34 +0100786 goto err_bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100787 }
788
789 fr_link->tx_cb = fr_tx_cb;
Harald Welte2cc1d4d2021-01-31 18:33:31 +0100790 fr_link->cb_data = bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100791 priv->link = fr_link;
Harald Welte41b188b2020-12-10 22:00:23 +0100792
Alexander Couzens6f89c772020-12-17 03:06:39 +0100793 priv->ifindex = rc = devname2ifindex(netif);
794 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100795 LOGBIND(bind, LOGL_ERROR, "Can not get interface index for interface %s\n", netif);
Harald Welte41b188b2020-12-10 22:00:23 +0100796 goto err_fr;
797 }
798
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100799 /* set protocol frame relay and lmi */
Harald Weltef2949742021-01-20 14:54:14 +0100800 rc = setup_device(priv->netif, bind);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100801 if(rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100802 LOGBIND(bind, LOGL_ERROR, "Failed to setup the interface %s for frame relay and lmi\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100803 goto err_fr;
804 }
805
Harald Weltef2949742021-01-20 14:54:14 +0100806 rc = open_socket(priv->ifindex, bind);
Alexander Couzens841817e2020-11-19 00:41:29 +0100807 if (rc < 0)
808 goto err_fr;
Harald Welted06128d2021-01-30 21:17:21 +0100809 INIT_LLIST_HEAD(&priv->backlog.list);
810 priv->backlog.retry_us = 2500; /* start with some non-zero value; this corrsponds to 496 bytes */
811 osmo_timer_setup(&priv->backlog.timer, fr_backlog_timer_cb, bind);
812 osmo_fd_setup(&priv->backlog.ofd, rc, OSMO_FD_READ, fr_netif_ofd_cb, bind, 0);
813 rc = osmo_fd_register(&priv->backlog.ofd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100814 if (rc < 0)
815 goto err_fd;
816
Harald Welte56f08a32020-12-01 23:07:32 +0100817#ifdef ENABLE_LIBMNL
818 if (!nsi->linkmon_mnl)
819 nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi);
820
821 /* we get a new full dump after every bind. which is a bit excessive. But that's just once
822 * at start-up, so we can get away with it */
823 if (nsi->linkmon_mnl)
824 linkmon_initial_dump(nsi->linkmon_mnl);
825#endif
826
Harald Weltec3aa8f92021-01-31 11:41:34 +0100827 if (result)
828 *result = bind;
829
Alexander Couzens841817e2020-11-19 00:41:29 +0100830 return rc;
831
832err_fd:
Harald Welted06128d2021-01-30 21:17:21 +0100833 close(priv->backlog.ofd.fd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100834err_fr:
835 osmo_fr_link_free(fr_link);
Alexander Couzens841817e2020-11-19 00:41:29 +0100836err_bind:
Harald Weltec3aa8f92021-01-31 11:41:34 +0100837 gprs_ns2_free_bind(bind);
Alexander Couzens841817e2020-11-19 00:41:29 +0100838
839 return rc;
840}
841
Alexander Couzensc782cec2020-12-10 04:10:25 +0100842/*! Return the frame relay role of a bind
843 * \param[in] bind The bind
844 * \return the frame relay role or -EINVAL if bind is not frame relay
845 */
846enum osmo_fr_role gprs_ns2_fr_bind_role(struct gprs_ns2_vc_bind *bind)
847{
848 struct priv_bind *priv;
849
850 if (bind->driver != &vc_driver_fr)
851 return -EINVAL;
852
853 priv = bind->priv;
854 return priv->link->role;
855}
856
Alexander Couzens841817e2020-11-19 00:41:29 +0100857/*! Return the network interface of the bind
858 * \param[in] bind The bind
859 * \return the network interface
860 */
861const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)
862{
863 struct priv_bind *priv;
864
865 if (bind->driver != &vc_driver_fr)
866 return NULL;
867
868 priv = bind->priv;
869 return priv->netif;
870}
871
872/*! Find NS bind for a given network interface
873 * \param[in] nsi NS instance
874 * \param[in] netif the network interface to search for
875 * \return the bind or NULL if not found
876 */
877struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
878 struct gprs_ns2_inst *nsi,
879 const char *netif)
880{
881 struct gprs_ns2_vc_bind *bind;
882 const char *_netif;
883
884 OSMO_ASSERT(nsi);
885 OSMO_ASSERT(netif);
886
887 llist_for_each_entry(bind, &nsi->binding, list) {
888 if (!gprs_ns2_is_fr_bind(bind))
889 continue;
890
891 _netif = gprs_ns2_fr_bind_netif(bind);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100892 if (!strncmp(_netif, netif, IFNAMSIZ))
Alexander Couzens841817e2020-11-19 00:41:29 +0100893 return bind;
894 }
895
896 return NULL;
897}
898
899/*! Create, connect and activate a new FR-based NS-VC
900 * \param[in] bind bind in which the new NS-VC is to be created
901 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
902 * \param[in] dlci Data Link connection identifier
903 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
904struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
Alexander Couzensebcbd722020-12-03 06:11:39 +0100905 struct gprs_ns2_nse *nse,
906 uint16_t nsvci,
907 uint16_t dlci)
908{
909 struct gprs_ns2_vc *nsvc = NULL;
910 struct priv_vc *priv = NULL;
Harald Welte603f4042020-11-29 17:39:19 +0100911 struct priv_bind *bpriv = bind->priv;
912 char idbuf[64];
Alexander Couzensebcbd722020-12-03 06:11:39 +0100913
Alexander Couzens55bc8692021-01-18 18:39:57 +0100914 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzensebcbd722020-12-03 06:11:39 +0100915 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
916 if (nsvc) {
917 goto err;
918 }
919
Harald Welte603f4042020-11-29 17:39:19 +0100920 snprintf(idbuf, sizeof(idbuf), "%s-%s-DLCI%u-NSE%05u-NSVC%05u", gprs_ns2_lltype_str(nse->ll),
921 bpriv->netif, dlci, nse->nsei, nsvci);
Alexander Couzens138b96f2021-01-25 16:23:29 +0100922 nsvc = ns2_vc_alloc(bind, nse, true, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
Alexander Couzensebcbd722020-12-03 06:11:39 +0100923 if (!nsvc)
924 goto err;
925
926 nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
927 if (!priv)
928 goto err;
929
930 nsvc->nsvci = nsvci;
931 nsvc->nsvci_is_valid = true;
932
Alexander Couzensebcbd722020-12-03 06:11:39 +0100933 return nsvc;
934
935err:
936 gprs_ns2_free_nsvc(nsvc);
937 return NULL;
938}
939
940
941/*! Create, connect and activate a new FR-based NS-VC
942 * \param[in] bind bind in which the new NS-VC is to be created
943 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
944 * \param[in] dlci Data Link connection identifier
945 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
946struct gprs_ns2_vc *gprs_ns2_fr_connect2(struct gprs_ns2_vc_bind *bind,
Alexander Couzens841817e2020-11-19 00:41:29 +0100947 uint16_t nsei,
948 uint16_t nsvci,
949 uint16_t dlci)
950{
951 bool created_nse = false;
952 struct gprs_ns2_vc *nsvc = NULL;
Alexander Couzens55bc8692021-01-18 18:39:57 +0100953 struct gprs_ns2_nse *nse;
954
955 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
956 nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
Alexander Couzens841817e2020-11-19 00:41:29 +0100957 if (!nse) {
Alexander Couzens138b96f2021-01-25 16:23:29 +0100958 nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_FR, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
Alexander Couzens841817e2020-11-19 00:41:29 +0100959 if (!nse)
960 return NULL;
961 created_nse = true;
962 }
963
Harald Welte509047b2021-01-17 19:55:51 +0100964 nsvc = gprs_ns2_fr_connect(bind, nse, nsvci, dlci);
Alexander Couzens841817e2020-11-19 00:41:29 +0100965 if (!nsvc)
966 goto err_nse;
967
Alexander Couzens841817e2020-11-19 00:41:29 +0100968 return nsvc;
969
Alexander Couzens841817e2020-11-19 00:41:29 +0100970err_nse:
971 if (created_nse)
972 gprs_ns2_free_nse(nse);
973
974 return NULL;
975}
976
977/*! Return the nsvc by dlci.
978 * \param[in] bind
979 * \param[in] dlci Data Link connection identifier
980 * \return the nsvc or NULL if not found
981 */
982struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,
983 uint16_t dlci)
984{
985 struct gprs_ns2_vc *nsvc;
986 struct priv_vc *vcpriv;
987
Alexander Couzens55bc8692021-01-18 18:39:57 +0100988 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100989 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
990 vcpriv = nsvc->priv;
991
992 if (dlci == vcpriv->dlci)
993 return nsvc;
994 }
995
996 return NULL;
997}
998
999/*! Return the dlci of the nsvc
1000 * \param[in] nsvc
1001 * \return the dlci or 0 on error. 0 is not a valid dlci.
1002 */
Alexander Couzens22c26e02020-12-10 04:10:07 +01001003uint16_t gprs_ns2_fr_nsvc_dlci(const struct gprs_ns2_vc *nsvc)
Alexander Couzens841817e2020-11-19 00:41:29 +01001004{
1005 struct priv_vc *vcpriv;
1006
1007 if (!nsvc->bind)
1008 return 0;
1009
1010 if (nsvc->bind->driver != &vc_driver_fr)
1011 return 0;
1012
1013 vcpriv = nsvc->priv;
1014 return vcpriv->dlci;
1015}