blob: fc6a5c4d63af63b293035a5f55e6052a062897ed [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;
108 /* list of msgb (backlog) */
109 struct llist_head list;
110 /* timer to trigger next attempt of AF_PACKET write */
111 struct osmo_timer_list timer;
112 /* re-try after that many micro-seconds */
113 uint32_t retry_us;
114 } backlog;
Alexander Couzens841817e2020-11-19 00:41:29 +0100115};
116
117struct priv_vc {
118 struct osmo_sockaddr remote;
119 uint16_t dlci;
120 struct osmo_fr_dlc *dlc;
121};
122
123static void free_vc(struct gprs_ns2_vc *nsvc)
124{
Alexander Couzensea377242021-01-17 16:51:55 +0100125 if (!nsvc)
126 return;
Alexander Couzens841817e2020-11-19 00:41:29 +0100127
128 if (!nsvc->priv)
129 return;
130
Alexander Couzens55bc8692021-01-18 18:39:57 +0100131 OSMO_ASSERT(gprs_ns2_is_fr_bind(nsvc->bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100132 talloc_free(nsvc->priv);
133 nsvc->priv = NULL;
134}
135
136static void dump_vty(const struct gprs_ns2_vc_bind *bind, struct vty *vty, bool _stats)
137{
138 struct priv_bind *priv;
139 struct gprs_ns2_vc *nsvc;
Harald Welte48bd76c2020-12-01 16:53:06 +0100140 struct osmo_fr_link *fr_link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100141
142 if (!bind)
143 return;
144
145 priv = bind->priv;
Harald Welte48bd76c2020-12-01 16:53:06 +0100146 fr_link = priv->link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100147
Harald Welte56f08a32020-12-01 23:07:32 +0100148 vty_out(vty, "FR bind: %s, role: %s, link: %s%s", priv->netif,
149 osmo_fr_role_str(fr_link->role), priv->if_running ? "UP" : "DOWN", VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100150
151 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
Harald Welte96ec84a2020-12-01 17:56:05 +0100152 vty_out(vty, " NSVCI %05u: %s%s", nsvc->nsvci, gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100153 }
154
155 priv = bind->priv;
156}
157
158/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
159static void free_bind(struct gprs_ns2_vc_bind *bind)
160{
161 struct priv_bind *priv;
Harald Welted06128d2021-01-30 21:17:21 +0100162 struct msgb *msg, *msg2;
Alexander Couzens841817e2020-11-19 00:41:29 +0100163
Alexander Couzens55bc8692021-01-18 18:39:57 +0100164 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100165 if (!bind)
166 return;
167
168 priv = bind->priv;
169
170 OSMO_ASSERT(llist_empty(&bind->nsvc));
171
Harald Welted06128d2021-01-30 21:17:21 +0100172 osmo_timer_del(&priv->backlog.timer);
173 llist_for_each_entry_safe(msg, msg2, &priv->backlog.list, list) {
174 msgb_free(msg);
175 }
176
Alexander Couzens841817e2020-11-19 00:41:29 +0100177 osmo_fr_link_free(priv->link);
Harald Welted06128d2021-01-30 21:17:21 +0100178 osmo_fd_close(&priv->backlog.ofd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100179 talloc_free(priv);
180}
181
Harald Welte9d28ce52021-01-31 18:38:20 +0100182static void fr_dlci_status_cb(struct osmo_fr_dlc *dlc, void *cb_data, bool active)
183{
184 struct gprs_ns2_vc *nsvc = cb_data;
185
186 if (active) {
187 ns2_vc_fsm_start(nsvc);
188 } else {
189 ns2_vc_force_unconfigured(nsvc);
190 }
191}
192
Alexander Couzens841817e2020-11-19 00:41:29 +0100193static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,
194 struct gprs_ns2_vc *nsvc,
195 uint16_t dlci)
196{
197 struct priv_bind *privb = bind->priv;
198 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
199 if (!priv)
200 return NULL;
201
Alexander Couzens55bc8692021-01-18 18:39:57 +0100202 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100203 nsvc->priv = priv;
204 priv->dlci = dlci;
205 priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);
206 if (!priv->dlc) {
207 nsvc->priv = NULL;
208 talloc_free(priv);
209 return NULL;
210 }
211
Harald Welte2cc1d4d2021-01-31 18:33:31 +0100212 priv->dlc->cb_data = nsvc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100213 priv->dlc->rx_cb = fr_dlci_rx_cb;
Harald Welte9d28ce52021-01-31 18:38:20 +0100214 priv->dlc->status_cb = fr_dlci_status_cb;
Alexander Couzens841817e2020-11-19 00:41:29 +0100215
216 return priv;
217}
218
219int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
220 uint16_t dlci,
221 struct gprs_ns2_vc **result)
222{
223 struct gprs_ns2_vc *nsvc;
224 struct priv_vc *vcpriv;
225
Alexander Couzens55bc8692021-01-18 18:39:57 +0100226 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100227 if (!result)
228 return -EINVAL;
229
230 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
231 vcpriv = nsvc->priv;
232 if (vcpriv->dlci != dlci) {
233 *result = nsvc;
234 return 0;
235 }
236 }
237
238 return 1;
239}
240
241/* PDU from the network interface towards the fr layer (upwards) */
Harald Welted06128d2021-01-30 21:17:21 +0100242static int fr_netif_ofd_cb(struct osmo_fd *bfd, uint32_t what)
Alexander Couzens841817e2020-11-19 00:41:29 +0100243{
244 struct gprs_ns2_vc_bind *bind = bfd->data;
245 struct priv_bind *priv = bind->priv;
Harald Welted06128d2021-01-30 21:17:21 +0100246 struct msgb *msg;
Harald Welte41b188b2020-12-10 22:00:23 +0100247 struct sockaddr_ll sll;
248 socklen_t sll_len = sizeof(sll);
Alexander Couzens841817e2020-11-19 00:41:29 +0100249 int rc = 0;
250
Harald Welted06128d2021-01-30 21:17:21 +0100251 /* we only handle read here. write to AF_PACKET sockets cannot be triggered
252 * by select or poll, see OS#4995 */
253 if (!(what & OSMO_FD_READ))
254 return 0;
255
256 msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
Alexander Couzens841817e2020-11-19 00:41:29 +0100257 if (!msg)
258 return -ENOMEM;
259
Harald Welte41b188b2020-12-10 22:00:23 +0100260 rc = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)&sll, &sll_len);
Alexander Couzens841817e2020-11-19 00:41:29 +0100261 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100262 LOGBIND(bind, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n", strerror(errno));
Alexander Couzens841817e2020-11-19 00:41:29 +0100263 goto out_err;
264 } else if (rc == 0) {
265 goto out_err;
266 }
267
Harald Welte41b188b2020-12-10 22:00:23 +0100268 /* ignore any packets that we might have received for a different interface, between
269 * the socket() and the bind() call */
270 if (sll.sll_ifindex != priv->ifindex)
271 goto out_err;
272
Alexander Couzens841817e2020-11-19 00:41:29 +0100273 msgb_put(msg, rc);
274 msg->dst = priv->link;
275 return osmo_fr_rx(msg);
276
277out_err:
278 msgb_free(msg);
279 return rc;
280}
281
282/* PDU from the frame relay towards the NS-VC (upwards) */
283static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)
284{
285 int rc;
286 struct gprs_ns2_vc *nsvc = cb_data;
287
288 rc = ns2_recv_vc(nsvc, msg);
289
290 return rc;
291}
292
Harald Welted06128d2021-01-30 21:17:21 +0100293static int fr_netif_write_one(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
Alexander Couzens841817e2020-11-19 00:41:29 +0100294{
Harald Welted06128d2021-01-30 21:17:21 +0100295 struct priv_bind *priv = bind->priv;
296 unsigned int len = msgb_length(msg);
Harald Weltef0073d72021-01-30 11:41:13 +0100297 int rc;
298
Harald Welted06128d2021-01-30 21:17:21 +0100299 /* estimate the retry time based on the data rate it takes to transmit */
300 priv->backlog.retry_us = (BIT_DURATION_NS * 8 * len) / 1000;
301
302 rc = write(priv->backlog.ofd.fd, msgb_data(msg), len);
303 if (rc == len) {
304 msgb_free(msg);
305 return 0;
306 } else if (rc < 0) {
307 /* don't free, the caller might want to re-transmit */
308 switch (errno) {
309 case EAGAIN:
310 case ENOBUFS:
311 return -errno;
312 default:
313 LOGBIND(bind, LOGL_ERROR, "error during write to AF_PACKET: %s\n", strerror(errno));
314 return -errno;
315 }
316 } else {
317 /* short write */
318 LOGBIND(bind, LOGL_ERROR, "short write on AF_PACKET: %d < %d\n", rc, len);
319 msgb_free(msg);
320 return 0;
321 }
Alexander Couzens841817e2020-11-19 00:41:29 +0100322}
323
324/*! determine if given bind is for FR-GRE encapsulation. */
325int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
326{
327 return (bind->driver == &vc_driver_fr);
328}
329
330/* PDU from the NS-VC towards the frame relay layer (downwards) */
331static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
332{
333 struct priv_vc *vcpriv = nsvc->priv;
334
335 msg->dst = vcpriv->dlc;
336 return osmo_fr_tx_dlc(msg);
337}
338
Harald Welted06128d2021-01-30 21:17:21 +0100339static void enqueue_at_head(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
340{
341 struct priv_bind *priv = bind->priv;
342 llist_add(&msg->list, &priv->backlog.list);
Harald Welte76346072021-01-31 11:54:02 +0100343 osmo_stat_item_inc(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100344 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
345}
346
347static void enqueue_at_tail(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
348{
349 struct priv_bind *priv = bind->priv;
350 llist_add_tail(&msg->list, &priv->backlog.list);
Harald Welte76346072021-01-31 11:54:02 +0100351 osmo_stat_item_inc(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100352 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
353}
354
355#define LMI_Q933A_DLCI 0
356
357/* enqueue to backlog (LMI, signaling) or drop (userdata msg) */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100358static int backlog_enqueue_or_free(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
Harald Welted06128d2021-01-30 21:17:21 +0100359{
360 uint8_t dlci = msg->data[0];
361 uint8_t ns_pdu_type;
362 uint16_t bvci;
363
364 if (msgb_length(msg) < 1)
365 goto out_free;
366
367 /* we want to enqueue only Q.933 LMI traffic or NS signaling; NOT user traffic */
368 switch (dlci) {
369 case LMI_Q933A_DLCI:
370 /* enqueue Q.933 LMI at head of queue */
371 enqueue_at_head(bind, msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100372 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100373 default:
374 if (msgb_length(msg) < 3)
375 break;
376 ns_pdu_type = msg->data[2];
377 switch (ns_pdu_type) {
378 case NS_PDUT_UNITDATA:
379 if (msgb_length(msg) < 6)
380 break;
381 bvci = osmo_load16be(msg->data + 4);
382 /* enqueue BVCI=0 traffic at tail of queue */
383 if (bvci == BVCI_SIGNALLING) {
384 enqueue_at_tail(bind, msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100385 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100386 }
387 break;
388 default:
389 /* enqueue NS signaling traffic at head of queue */
390 enqueue_at_head(bind, msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100391 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100392 }
393 break;
394 }
395
396out_free:
397 /* drop everything that is not LMI, NS-signaling or BVCI-0 */
398 msgb_free(msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100399 return -1;
Harald Welted06128d2021-01-30 21:17:21 +0100400}
401
402static void fr_backlog_timer_cb(void *data)
403{
404 struct gprs_ns2_vc_bind *bind = data;
405 struct priv_bind *priv = bind->priv;
406 int i, rc;
407
408 /* attempt to send up to 10 messages in every timer */
409 for (i = 0; i < 10; i++) {
410 struct msgb *msg = msgb_dequeue(&priv->backlog.list);
411 if (!msg)
412 break;
413
414 rc = fr_netif_write_one(bind, msg);
415 if (rc < 0) {
416 /* re-add at head of list */
417 llist_add(&msg->list, &priv->backlog.list);
418 break;
419 }
Harald Welte76346072021-01-31 11:54:02 +0100420 osmo_stat_item_dec(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100421 }
422
423 /* re-start timer if we still have data in the queue */
424 if (!llist_empty(&priv->backlog.list))
425 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
426}
427
Alexander Couzens841817e2020-11-19 00:41:29 +0100428/* PDU from the frame relay layer towards the network interface (downwards) */
429int fr_tx_cb(void *data, struct msgb *msg)
430{
431 struct gprs_ns2_vc_bind *bind = data;
432 struct priv_bind *priv = bind->priv;
Harald Welted06128d2021-01-30 21:17:21 +0100433 int rc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100434
Harald Welted06128d2021-01-30 21:17:21 +0100435 if (llist_empty(&priv->backlog.list)) {
436 /* attempt to transmit right now */
437 rc = fr_netif_write_one(bind, msg);
438 if (rc < 0) {
439 /* enqueue to backlog in case it fails */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100440 return backlog_enqueue_or_free(bind, msg);
Harald Welted06128d2021-01-30 21:17:21 +0100441 }
442 } else {
443 /* enqueue to backlog */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100444 return backlog_enqueue_or_free(bind, msg);
Alexander Couzens60021a42020-12-17 02:48:22 +0100445 }
Alexander Couzens841817e2020-11-19 00:41:29 +0100446
Alexander Couzens60021a42020-12-17 02:48:22 +0100447 return 0;
Alexander Couzens841817e2020-11-19 00:41:29 +0100448}
449
450static int devname2ifindex(const char *ifname)
451{
452 struct ifreq ifr;
453 int sk, rc;
454
455 sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
456 if (sk < 0)
457 return sk;
458
459
460 memset(&ifr, 0, sizeof(ifr));
Neels Hofmeyr475a0ac2020-12-17 18:10:34 +0100461 OSMO_STRLCPY_ARRAY(ifr.ifr_name, ifname);
Alexander Couzens841817e2020-11-19 00:41:29 +0100462
463 rc = ioctl(sk, SIOCGIFINDEX, &ifr);
464 close(sk);
465 if (rc < 0)
466 return rc;
467
468 return ifr.ifr_ifindex;
469}
470
Harald Weltef2949742021-01-20 14:54:14 +0100471static int open_socket(int ifindex, const struct gprs_ns2_vc_bind *nsbind)
Alexander Couzens841817e2020-11-19 00:41:29 +0100472{
473 struct sockaddr_ll addr;
Harald Welte4ed0f4e2020-12-10 21:50:32 +0100474 int fd, rc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100475
Alexander Couzens841817e2020-11-19 00:41:29 +0100476 memset(&addr, 0, sizeof(addr));
477 addr.sll_family = AF_PACKET;
478 addr.sll_protocol = htons(ETH_P_ALL);
479 addr.sll_ifindex = ifindex;
480
Harald Welte5bea72e2020-12-10 22:06:21 +0100481 fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_HDLC));
Alexander Couzens841817e2020-11-19 00:41:29 +0100482 if (fd < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100483 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 +0100484 return fd;
485 }
486
Harald Welte41b188b2020-12-10 22:00:23 +0100487 /* there's a race condition between the above syscall and the bind() call below,
488 * causing other packets to be received in between */
489
Alexander Couzens841817e2020-11-19 00:41:29 +0100490 rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
491 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100492 LOGBIND(nsbind, LOGL_ERROR, "Can not bind AF_PACKET socket to ifindex %d\n", ifindex);
Alexander Couzens841817e2020-11-19 00:41:29 +0100493 close(fd);
494 return rc;
495 }
496
497 return fd;
498}
499
Harald Welte56f08a32020-12-01 23:07:32 +0100500#ifdef ENABLE_LIBMNL
501
502#include <osmocom/core/mnl.h>
Harald Welte56f08a32020-12-01 23:07:32 +0100503#include <linux/if_link.h>
504#include <linux/rtnetlink.h>
505
506#ifndef ARPHRD_FRAD
507#define ARPHRD_FRAD 770
508#endif
509
510/* validate the netlink attributes */
511static int data_attr_cb(const struct nlattr *attr, void *data)
512{
513 const struct nlattr **tb = data;
514 int type = mnl_attr_get_type(attr);
515
516 if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
517 return MNL_CB_OK;
518
519 switch (type) {
520 case IFLA_MTU:
521 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
522 return MNL_CB_ERROR;
523 break;
524 case IFLA_IFNAME:
525 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
526 return MNL_CB_ERROR;
527 break;
528 }
529 tb[type] = attr;
530 return MNL_CB_OK;
531}
532
533/* find the bind for the netdev (if any) */
534static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname)
535{
536 struct gprs_ns2_vc_bind *bind;
537
538 llist_for_each_entry(bind, &nsi->binding, list) {
539 struct priv_bind *bpriv = bind->priv;
540 if (!strcmp(bpriv->netif, ifname))
541 return bind;
542 }
543
544 return NULL;
545}
546
547/* handle a single netlink message received via libmnl */
548static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data)
549{
550 struct osmo_mnl *omnl = data;
551 struct gprs_ns2_vc_bind *bind;
552 struct nlattr *tb[IFLA_MAX+1] = {};
553 struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
554 struct gprs_ns2_inst *nsi;
555 const char *ifname;
556 bool if_running;
557
558 OSMO_ASSERT(omnl);
559 OSMO_ASSERT(ifm);
560
561 nsi = omnl->priv;
562
563 if (ifm->ifi_type != ARPHRD_FRAD)
564 return MNL_CB_OK;
565
566 mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
567
568 if (!tb[IFLA_IFNAME])
569 return MNL_CB_OK;
570 ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
571 if_running = !!(ifm->ifi_flags & IFF_RUNNING);
572
573 bind = bind4netdev(nsi, ifname);
574 if (bind) {
575 struct priv_bind *bpriv = bind->priv;
576 if (bpriv->if_running != if_running) {
577 /* update running state */
Harald Weltef2949742021-01-20 14:54:14 +0100578 LOGBIND(bind, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
579 ifname, if_running ? "UP" : "DOWN");
Harald Welte56f08a32020-12-01 23:07:32 +0100580 bpriv->if_running = if_running;
581 }
582 }
583
584 return MNL_CB_OK;
585}
586
587/* trigger one initial dump of all link information */
588static void linkmon_initial_dump(struct osmo_mnl *omnl)
589{
590 char buf[MNL_SOCKET_BUFFER_SIZE];
591 struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
592 struct rtgenmsg *rt;
593
594 nlh->nlmsg_type = RTM_GETLINK;
595 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
596 nlh->nlmsg_seq = time(NULL);
597 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
598 rt->rtgen_family = AF_PACKET;
599
600 if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
601 LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno));
602 }
603
604 /* the response[s] will be handled just like the events */
605}
606#endif /* LIBMNL */
607
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100608static int set_ifupdown(const char *netif, bool up)
609{
610 int sock, rc;
611 struct ifreq req;
612
613 sock = socket(AF_INET, SOCK_DGRAM, 0);
614 if (sock < 0)
615 return sock;
616
617 memset(&req, 0, sizeof req);
Harald Welte7f01b682020-12-21 12:39:38 +0100618 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100619
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100620 rc = ioctl(sock, SIOCGIFFLAGS, &req);
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100621 if (rc < 0) {
622 close(sock);
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100623 return rc;
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100624 }
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100625
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100626 if ((req.ifr_flags & IFF_UP) == up) {
627 close(sock);
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100628 return 0;
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100629 }
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100630
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100631 if (up)
632 req.ifr_flags |= IFF_UP;
633
634 rc = ioctl(sock, SIOCSIFFLAGS, &req);
635 close(sock);
636 return rc;
637}
638
Harald Weltef2949742021-01-20 14:54:14 +0100639static int setup_device(const char *netif, const struct gprs_ns2_vc_bind *bind)
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100640{
641 int sock, rc;
642 char buffer[128];
643 fr_proto *fr = (void*)buffer;
644 struct ifreq req;
645
646 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
647 if (sock < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100648 LOGBIND(bind, LOGL_ERROR, "%s: Unable to create socket: %s\n",
649 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100650 return sock;
651 }
652
653 memset(&req, 0, sizeof(struct ifreq));
654 memset(&buffer, 0, sizeof(buffer));
Harald Welteb8de1882020-12-21 12:40:45 +0100655 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100656 req.ifr_settings.ifs_ifsu.sync = (void*)buffer;
657 req.ifr_settings.size = sizeof(buffer);
658 req.ifr_settings.type = IF_GET_PROTO;
659
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100660 /* EINVAL is returned when no protocol has been set */
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100661 rc = ioctl(sock, SIOCWANDEV, &req);
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100662 if (rc < 0 && errno != EINVAL) {
Harald Weltef2949742021-01-20 14:54:14 +0100663 LOGBIND(bind, LOGL_ERROR, "%s: Unable to get FR protocol information: %s\n",
664 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100665 goto err;
666 }
667
668 /* check if the device is good */
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100669 if (rc == 0 && req.ifr_settings.type == IF_PROTO_FR && fr->lmi == LMI_NONE) {
Harald Weltef2949742021-01-20 14:54:14 +0100670 LOGBIND(bind, LOGL_NOTICE, "%s: has correct frame relay mode and lmi\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100671 goto ifup;
672 }
673
674 /* modify the device to match */
675 rc = set_ifupdown(netif, false);
676 if (rc) {
Harald Weltef2949742021-01-20 14:54:14 +0100677 LOGBIND(bind, LOGL_ERROR, "Unable to bring down the device %s: %s\n",
678 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100679 goto err;
680 }
681
682 memset(&req, 0, sizeof(struct ifreq));
683 memset(fr, 0, sizeof(fr_proto));
Harald Welteb8de1882020-12-21 12:40:45 +0100684 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100685 req.ifr_settings.type = IF_PROTO_FR;
686 req.ifr_settings.size = sizeof(fr_proto);
687 req.ifr_settings.ifs_ifsu.fr = fr;
688 fr->lmi = LMI_NONE;
689 /* even those settings aren't used, they must be in the range */
690 /* polling verification timer*/
691 fr->t391 = 10;
692 /* link integrity verification polling timer */
693 fr->t392 = 15;
694 /* full status polling counter*/
695 fr->n391 = 6;
696 /* error threshold */
697 fr->n392 = 3;
698 /* monitored events count */
699 fr->n393 = 4;
700
Harald Weltef2949742021-01-20 14:54:14 +0100701 LOGBIND(bind, LOGL_INFO, "%s: Setting frame relay related parameters\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100702 rc = ioctl(sock, SIOCWANDEV, &req);
703 if (rc) {
Harald Weltef2949742021-01-20 14:54:14 +0100704 LOGBIND(bind, LOGL_ERROR, "%s: Unable to set FR protocol on information: %s\n",
705 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100706 goto err;
707 }
708
709ifup:
710 rc = set_ifupdown(netif, true);
711 if (rc)
Harald Weltef2949742021-01-20 14:54:14 +0100712 LOGBIND(bind, LOGL_ERROR, "Unable to bring up the device %s: %s\n",
713 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100714err:
715 close(sock);
716 return rc;
717}
Harald Welte56f08a32020-12-01 23:07:32 +0100718
Alexander Couzens841817e2020-11-19 00:41:29 +0100719/*! Create a new bind for NS over FR.
720 * \param[in] nsi NS instance in which to create the bind
721 * \param[in] netif Network interface to bind to
722 * \param[in] fr_network
723 * \param[in] fr_role
724 * \param[out] result pointer to created bind
725 * \return 0 on success; negative on error */
726int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100727 const char *name,
Alexander Couzens841817e2020-11-19 00:41:29 +0100728 const char *netif,
729 struct osmo_fr_network *fr_network,
730 enum osmo_fr_role fr_role,
731 struct gprs_ns2_vc_bind **result)
732{
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100733 struct gprs_ns2_vc_bind *bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100734 struct priv_bind *priv;
735 struct osmo_fr_link *fr_link;
736 int rc = 0;
737
Harald Weltec3aa8f92021-01-31 11:41:34 +0100738 if (strlen(netif) > IFNAMSIZ)
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100739 return -EINVAL;
740
741 if (gprs_ns2_bind_by_name(nsi, name))
742 return -EALREADY;
743
Harald Weltec3aa8f92021-01-31 11:41:34 +0100744 rc = ns2_bind_alloc(nsi, name, &bind);
745 if (rc < 0)
746 return rc;
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100747
Alexander Couzens841817e2020-11-19 00:41:29 +0100748 bind->driver = &vc_driver_fr;
Alexander Couzensaac90162020-11-19 02:44:04 +0100749 bind->ll = GPRS_NS2_LL_FR;
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100750 /* 2 mbit */
751 bind->transfer_capability = 2;
Alexander Couzens841817e2020-11-19 00:41:29 +0100752 bind->send_vc = fr_vc_sendmsg;
753 bind->free_vc = free_vc;
754 bind->dump_vty = dump_vty;
Alexander Couzens841817e2020-11-19 00:41:29 +0100755 priv = bind->priv = talloc_zero(bind, struct priv_bind);
756 if (!priv) {
Harald Weltebdfb8b92021-01-31 11:44:57 +0100757 rc = -ENOMEM;
Harald Weltec3aa8f92021-01-31 11:41:34 +0100758 goto err_bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100759 }
760
Neels Hofmeyr8fef7612020-12-17 18:19:19 +0100761 OSMO_STRLCPY_ARRAY(priv->netif, netif);
Alexander Couzens841817e2020-11-19 00:41:29 +0100762
Alexander Couzens841817e2020-11-19 00:41:29 +0100763 /* FIXME: move fd handling into socket.c */
764 fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);
765 if (!fr_link) {
766 rc = -EINVAL;
Harald Weltec3aa8f92021-01-31 11:41:34 +0100767 goto err_bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100768 }
769
770 fr_link->tx_cb = fr_tx_cb;
Harald Welte2cc1d4d2021-01-31 18:33:31 +0100771 fr_link->cb_data = bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100772 priv->link = fr_link;
Harald Welte41b188b2020-12-10 22:00:23 +0100773
Alexander Couzens6f89c772020-12-17 03:06:39 +0100774 priv->ifindex = rc = devname2ifindex(netif);
775 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100776 LOGBIND(bind, LOGL_ERROR, "Can not get interface index for interface %s\n", netif);
Harald Welte41b188b2020-12-10 22:00:23 +0100777 goto err_fr;
778 }
779
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100780 /* set protocol frame relay and lmi */
Harald Weltef2949742021-01-20 14:54:14 +0100781 rc = setup_device(priv->netif, bind);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100782 if(rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100783 LOGBIND(bind, LOGL_ERROR, "Failed to setup the interface %s for frame relay and lmi\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100784 goto err_fr;
785 }
786
Harald Weltef2949742021-01-20 14:54:14 +0100787 rc = open_socket(priv->ifindex, bind);
Alexander Couzens841817e2020-11-19 00:41:29 +0100788 if (rc < 0)
789 goto err_fr;
Harald Welted06128d2021-01-30 21:17:21 +0100790 INIT_LLIST_HEAD(&priv->backlog.list);
791 priv->backlog.retry_us = 2500; /* start with some non-zero value; this corrsponds to 496 bytes */
792 osmo_timer_setup(&priv->backlog.timer, fr_backlog_timer_cb, bind);
793 osmo_fd_setup(&priv->backlog.ofd, rc, OSMO_FD_READ, fr_netif_ofd_cb, bind, 0);
794 rc = osmo_fd_register(&priv->backlog.ofd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100795 if (rc < 0)
796 goto err_fd;
797
Harald Welte56f08a32020-12-01 23:07:32 +0100798#ifdef ENABLE_LIBMNL
799 if (!nsi->linkmon_mnl)
800 nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi);
801
802 /* we get a new full dump after every bind. which is a bit excessive. But that's just once
803 * at start-up, so we can get away with it */
804 if (nsi->linkmon_mnl)
805 linkmon_initial_dump(nsi->linkmon_mnl);
806#endif
807
Harald Weltec3aa8f92021-01-31 11:41:34 +0100808 if (result)
809 *result = bind;
810
Alexander Couzens841817e2020-11-19 00:41:29 +0100811 return rc;
812
813err_fd:
Harald Welted06128d2021-01-30 21:17:21 +0100814 close(priv->backlog.ofd.fd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100815err_fr:
816 osmo_fr_link_free(fr_link);
Alexander Couzens841817e2020-11-19 00:41:29 +0100817err_bind:
Harald Weltec3aa8f92021-01-31 11:41:34 +0100818 gprs_ns2_free_bind(bind);
Alexander Couzens841817e2020-11-19 00:41:29 +0100819
820 return rc;
821}
822
Alexander Couzensc782cec2020-12-10 04:10:25 +0100823/*! Return the frame relay role of a bind
824 * \param[in] bind The bind
825 * \return the frame relay role or -EINVAL if bind is not frame relay
826 */
827enum osmo_fr_role gprs_ns2_fr_bind_role(struct gprs_ns2_vc_bind *bind)
828{
829 struct priv_bind *priv;
830
831 if (bind->driver != &vc_driver_fr)
832 return -EINVAL;
833
834 priv = bind->priv;
835 return priv->link->role;
836}
837
Alexander Couzens841817e2020-11-19 00:41:29 +0100838/*! Return the network interface of the bind
839 * \param[in] bind The bind
840 * \return the network interface
841 */
842const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)
843{
844 struct priv_bind *priv;
845
846 if (bind->driver != &vc_driver_fr)
847 return NULL;
848
849 priv = bind->priv;
850 return priv->netif;
851}
852
853/*! Find NS bind for a given network interface
854 * \param[in] nsi NS instance
855 * \param[in] netif the network interface to search for
856 * \return the bind or NULL if not found
857 */
858struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
859 struct gprs_ns2_inst *nsi,
860 const char *netif)
861{
862 struct gprs_ns2_vc_bind *bind;
863 const char *_netif;
864
865 OSMO_ASSERT(nsi);
866 OSMO_ASSERT(netif);
867
868 llist_for_each_entry(bind, &nsi->binding, list) {
869 if (!gprs_ns2_is_fr_bind(bind))
870 continue;
871
872 _netif = gprs_ns2_fr_bind_netif(bind);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100873 if (!strncmp(_netif, netif, IFNAMSIZ))
Alexander Couzens841817e2020-11-19 00:41:29 +0100874 return bind;
875 }
876
877 return NULL;
878}
879
880/*! Create, connect and activate a new FR-based NS-VC
881 * \param[in] bind bind in which the new NS-VC is to be created
882 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
883 * \param[in] dlci Data Link connection identifier
884 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
885struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
Alexander Couzensebcbd722020-12-03 06:11:39 +0100886 struct gprs_ns2_nse *nse,
887 uint16_t nsvci,
888 uint16_t dlci)
889{
890 struct gprs_ns2_vc *nsvc = NULL;
891 struct priv_vc *priv = NULL;
Harald Welte603f4042020-11-29 17:39:19 +0100892 struct priv_bind *bpriv = bind->priv;
893 char idbuf[64];
Alexander Couzensebcbd722020-12-03 06:11:39 +0100894
Alexander Couzens55bc8692021-01-18 18:39:57 +0100895 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzensebcbd722020-12-03 06:11:39 +0100896 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
897 if (nsvc) {
898 goto err;
899 }
900
Harald Welte603f4042020-11-29 17:39:19 +0100901 snprintf(idbuf, sizeof(idbuf), "%s-%s-DLCI%u-NSE%05u-NSVC%05u", gprs_ns2_lltype_str(nse->ll),
902 bpriv->netif, dlci, nse->nsei, nsvci);
Alexander Couzens138b96f2021-01-25 16:23:29 +0100903 nsvc = ns2_vc_alloc(bind, nse, true, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
Alexander Couzensebcbd722020-12-03 06:11:39 +0100904 if (!nsvc)
905 goto err;
906
907 nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
908 if (!priv)
909 goto err;
910
911 nsvc->nsvci = nsvci;
912 nsvc->nsvci_is_valid = true;
913
Alexander Couzensebcbd722020-12-03 06:11:39 +0100914 return nsvc;
915
916err:
917 gprs_ns2_free_nsvc(nsvc);
918 return NULL;
919}
920
921
922/*! Create, connect and activate a new FR-based NS-VC
923 * \param[in] bind bind in which the new NS-VC is to be created
924 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
925 * \param[in] dlci Data Link connection identifier
926 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
927struct gprs_ns2_vc *gprs_ns2_fr_connect2(struct gprs_ns2_vc_bind *bind,
Alexander Couzens841817e2020-11-19 00:41:29 +0100928 uint16_t nsei,
929 uint16_t nsvci,
930 uint16_t dlci)
931{
932 bool created_nse = false;
933 struct gprs_ns2_vc *nsvc = NULL;
Alexander Couzens55bc8692021-01-18 18:39:57 +0100934 struct gprs_ns2_nse *nse;
935
936 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
937 nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
Alexander Couzens841817e2020-11-19 00:41:29 +0100938 if (!nse) {
Alexander Couzens138b96f2021-01-25 16:23:29 +0100939 nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_FR, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
Alexander Couzens841817e2020-11-19 00:41:29 +0100940 if (!nse)
941 return NULL;
942 created_nse = true;
943 }
944
Harald Welte509047b2021-01-17 19:55:51 +0100945 nsvc = gprs_ns2_fr_connect(bind, nse, nsvci, dlci);
Alexander Couzens841817e2020-11-19 00:41:29 +0100946 if (!nsvc)
947 goto err_nse;
948
Alexander Couzens841817e2020-11-19 00:41:29 +0100949 return nsvc;
950
Alexander Couzens841817e2020-11-19 00:41:29 +0100951err_nse:
952 if (created_nse)
953 gprs_ns2_free_nse(nse);
954
955 return NULL;
956}
957
958/*! Return the nsvc by dlci.
959 * \param[in] bind
960 * \param[in] dlci Data Link connection identifier
961 * \return the nsvc or NULL if not found
962 */
963struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,
964 uint16_t dlci)
965{
966 struct gprs_ns2_vc *nsvc;
967 struct priv_vc *vcpriv;
968
Alexander Couzens55bc8692021-01-18 18:39:57 +0100969 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100970 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
971 vcpriv = nsvc->priv;
972
973 if (dlci == vcpriv->dlci)
974 return nsvc;
975 }
976
977 return NULL;
978}
979
980/*! Return the dlci of the nsvc
981 * \param[in] nsvc
982 * \return the dlci or 0 on error. 0 is not a valid dlci.
983 */
Alexander Couzens22c26e02020-12-10 04:10:07 +0100984uint16_t gprs_ns2_fr_nsvc_dlci(const struct gprs_ns2_vc *nsvc)
Alexander Couzens841817e2020-11-19 00:41:29 +0100985{
986 struct priv_vc *vcpriv;
987
988 if (!nsvc->bind)
989 return 0;
990
991 if (nsvc->bind->driver != &vc_driver_fr)
992 return 0;
993
994 vcpriv = nsvc->priv;
995 return vcpriv->dlci;
996}