blob: b5ddf7e93d424f1ad9314c822bd9b86433e702b5 [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
182static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,
183 struct gprs_ns2_vc *nsvc,
184 uint16_t dlci)
185{
186 struct priv_bind *privb = bind->priv;
187 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
188 if (!priv)
189 return NULL;
190
Alexander Couzens55bc8692021-01-18 18:39:57 +0100191 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100192 nsvc->priv = priv;
193 priv->dlci = dlci;
194 priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);
195 if (!priv->dlc) {
196 nsvc->priv = NULL;
197 talloc_free(priv);
198 return NULL;
199 }
200
Harald Welte2cc1d4d2021-01-31 18:33:31 +0100201 priv->dlc->cb_data = nsvc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100202 priv->dlc->rx_cb = fr_dlci_rx_cb;
203
204 return priv;
205}
206
207int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
208 uint16_t dlci,
209 struct gprs_ns2_vc **result)
210{
211 struct gprs_ns2_vc *nsvc;
212 struct priv_vc *vcpriv;
213
Alexander Couzens55bc8692021-01-18 18:39:57 +0100214 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100215 if (!result)
216 return -EINVAL;
217
218 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
219 vcpriv = nsvc->priv;
220 if (vcpriv->dlci != dlci) {
221 *result = nsvc;
222 return 0;
223 }
224 }
225
226 return 1;
227}
228
229/* PDU from the network interface towards the fr layer (upwards) */
Harald Welted06128d2021-01-30 21:17:21 +0100230static int fr_netif_ofd_cb(struct osmo_fd *bfd, uint32_t what)
Alexander Couzens841817e2020-11-19 00:41:29 +0100231{
232 struct gprs_ns2_vc_bind *bind = bfd->data;
233 struct priv_bind *priv = bind->priv;
Harald Welted06128d2021-01-30 21:17:21 +0100234 struct msgb *msg;
Harald Welte41b188b2020-12-10 22:00:23 +0100235 struct sockaddr_ll sll;
236 socklen_t sll_len = sizeof(sll);
Alexander Couzens841817e2020-11-19 00:41:29 +0100237 int rc = 0;
238
Harald Welted06128d2021-01-30 21:17:21 +0100239 /* we only handle read here. write to AF_PACKET sockets cannot be triggered
240 * by select or poll, see OS#4995 */
241 if (!(what & OSMO_FD_READ))
242 return 0;
243
244 msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
Alexander Couzens841817e2020-11-19 00:41:29 +0100245 if (!msg)
246 return -ENOMEM;
247
Harald Welte41b188b2020-12-10 22:00:23 +0100248 rc = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)&sll, &sll_len);
Alexander Couzens841817e2020-11-19 00:41:29 +0100249 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100250 LOGBIND(bind, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n", strerror(errno));
Alexander Couzens841817e2020-11-19 00:41:29 +0100251 goto out_err;
252 } else if (rc == 0) {
253 goto out_err;
254 }
255
Harald Welte41b188b2020-12-10 22:00:23 +0100256 /* ignore any packets that we might have received for a different interface, between
257 * the socket() and the bind() call */
258 if (sll.sll_ifindex != priv->ifindex)
259 goto out_err;
260
Alexander Couzens841817e2020-11-19 00:41:29 +0100261 msgb_put(msg, rc);
262 msg->dst = priv->link;
263 return osmo_fr_rx(msg);
264
265out_err:
266 msgb_free(msg);
267 return rc;
268}
269
270/* PDU from the frame relay towards the NS-VC (upwards) */
271static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)
272{
273 int rc;
274 struct gprs_ns2_vc *nsvc = cb_data;
275
276 rc = ns2_recv_vc(nsvc, msg);
277
278 return rc;
279}
280
Harald Welted06128d2021-01-30 21:17:21 +0100281static int fr_netif_write_one(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
Alexander Couzens841817e2020-11-19 00:41:29 +0100282{
Harald Welted06128d2021-01-30 21:17:21 +0100283 struct priv_bind *priv = bind->priv;
284 unsigned int len = msgb_length(msg);
Harald Weltef0073d72021-01-30 11:41:13 +0100285 int rc;
286
Harald Welted06128d2021-01-30 21:17:21 +0100287 /* estimate the retry time based on the data rate it takes to transmit */
288 priv->backlog.retry_us = (BIT_DURATION_NS * 8 * len) / 1000;
289
290 rc = write(priv->backlog.ofd.fd, msgb_data(msg), len);
291 if (rc == len) {
292 msgb_free(msg);
293 return 0;
294 } else if (rc < 0) {
295 /* don't free, the caller might want to re-transmit */
296 switch (errno) {
297 case EAGAIN:
298 case ENOBUFS:
299 return -errno;
300 default:
301 LOGBIND(bind, LOGL_ERROR, "error during write to AF_PACKET: %s\n", strerror(errno));
302 return -errno;
303 }
304 } else {
305 /* short write */
306 LOGBIND(bind, LOGL_ERROR, "short write on AF_PACKET: %d < %d\n", rc, len);
307 msgb_free(msg);
308 return 0;
309 }
Alexander Couzens841817e2020-11-19 00:41:29 +0100310}
311
312/*! determine if given bind is for FR-GRE encapsulation. */
313int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
314{
315 return (bind->driver == &vc_driver_fr);
316}
317
318/* PDU from the NS-VC towards the frame relay layer (downwards) */
319static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
320{
321 struct priv_vc *vcpriv = nsvc->priv;
322
323 msg->dst = vcpriv->dlc;
324 return osmo_fr_tx_dlc(msg);
325}
326
Harald Welted06128d2021-01-30 21:17:21 +0100327static void enqueue_at_head(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
328{
329 struct priv_bind *priv = bind->priv;
330 llist_add(&msg->list, &priv->backlog.list);
Harald Welte76346072021-01-31 11:54:02 +0100331 osmo_stat_item_inc(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100332 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
333}
334
335static void enqueue_at_tail(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
336{
337 struct priv_bind *priv = bind->priv;
338 llist_add_tail(&msg->list, &priv->backlog.list);
Harald Welte76346072021-01-31 11:54:02 +0100339 osmo_stat_item_inc(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100340 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
341}
342
343#define LMI_Q933A_DLCI 0
344
345/* enqueue to backlog (LMI, signaling) or drop (userdata msg) */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100346static int backlog_enqueue_or_free(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
Harald Welted06128d2021-01-30 21:17:21 +0100347{
348 uint8_t dlci = msg->data[0];
349 uint8_t ns_pdu_type;
350 uint16_t bvci;
351
352 if (msgb_length(msg) < 1)
353 goto out_free;
354
355 /* we want to enqueue only Q.933 LMI traffic or NS signaling; NOT user traffic */
356 switch (dlci) {
357 case LMI_Q933A_DLCI:
358 /* enqueue Q.933 LMI at head of queue */
359 enqueue_at_head(bind, msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100360 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100361 default:
362 if (msgb_length(msg) < 3)
363 break;
364 ns_pdu_type = msg->data[2];
365 switch (ns_pdu_type) {
366 case NS_PDUT_UNITDATA:
367 if (msgb_length(msg) < 6)
368 break;
369 bvci = osmo_load16be(msg->data + 4);
370 /* enqueue BVCI=0 traffic at tail of queue */
371 if (bvci == BVCI_SIGNALLING) {
372 enqueue_at_tail(bind, msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100373 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100374 }
375 break;
376 default:
377 /* enqueue NS signaling traffic at head of queue */
378 enqueue_at_head(bind, msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100379 return 0;
Harald Welted06128d2021-01-30 21:17:21 +0100380 }
381 break;
382 }
383
384out_free:
385 /* drop everything that is not LMI, NS-signaling or BVCI-0 */
386 msgb_free(msg);
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100387 return -1;
Harald Welted06128d2021-01-30 21:17:21 +0100388}
389
390static void fr_backlog_timer_cb(void *data)
391{
392 struct gprs_ns2_vc_bind *bind = data;
393 struct priv_bind *priv = bind->priv;
394 int i, rc;
395
396 /* attempt to send up to 10 messages in every timer */
397 for (i = 0; i < 10; i++) {
398 struct msgb *msg = msgb_dequeue(&priv->backlog.list);
399 if (!msg)
400 break;
401
402 rc = fr_netif_write_one(bind, msg);
403 if (rc < 0) {
404 /* re-add at head of list */
405 llist_add(&msg->list, &priv->backlog.list);
406 break;
407 }
Harald Welte76346072021-01-31 11:54:02 +0100408 osmo_stat_item_dec(bind->statg->items[NS2_BIND_STAT_BACKLOG_LEN], 1);
Harald Welted06128d2021-01-30 21:17:21 +0100409 }
410
411 /* re-start timer if we still have data in the queue */
412 if (!llist_empty(&priv->backlog.list))
413 osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
414}
415
Alexander Couzens841817e2020-11-19 00:41:29 +0100416/* PDU from the frame relay layer towards the network interface (downwards) */
417int fr_tx_cb(void *data, struct msgb *msg)
418{
419 struct gprs_ns2_vc_bind *bind = data;
420 struct priv_bind *priv = bind->priv;
Harald Welted06128d2021-01-30 21:17:21 +0100421 int rc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100422
Harald Welted06128d2021-01-30 21:17:21 +0100423 if (llist_empty(&priv->backlog.list)) {
424 /* attempt to transmit right now */
425 rc = fr_netif_write_one(bind, msg);
426 if (rc < 0) {
427 /* enqueue to backlog in case it fails */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100428 return backlog_enqueue_or_free(bind, msg);
Harald Welted06128d2021-01-30 21:17:21 +0100429 }
430 } else {
431 /* enqueue to backlog */
Harald Welte5e0ef6f2021-01-30 22:03:26 +0100432 return backlog_enqueue_or_free(bind, msg);
Alexander Couzens60021a42020-12-17 02:48:22 +0100433 }
Alexander Couzens841817e2020-11-19 00:41:29 +0100434
Alexander Couzens60021a42020-12-17 02:48:22 +0100435 return 0;
Alexander Couzens841817e2020-11-19 00:41:29 +0100436}
437
438static int devname2ifindex(const char *ifname)
439{
440 struct ifreq ifr;
441 int sk, rc;
442
443 sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
444 if (sk < 0)
445 return sk;
446
447
448 memset(&ifr, 0, sizeof(ifr));
Neels Hofmeyr475a0ac2020-12-17 18:10:34 +0100449 OSMO_STRLCPY_ARRAY(ifr.ifr_name, ifname);
Alexander Couzens841817e2020-11-19 00:41:29 +0100450
451 rc = ioctl(sk, SIOCGIFINDEX, &ifr);
452 close(sk);
453 if (rc < 0)
454 return rc;
455
456 return ifr.ifr_ifindex;
457}
458
Harald Weltef2949742021-01-20 14:54:14 +0100459static int open_socket(int ifindex, const struct gprs_ns2_vc_bind *nsbind)
Alexander Couzens841817e2020-11-19 00:41:29 +0100460{
461 struct sockaddr_ll addr;
Harald Welte4ed0f4e2020-12-10 21:50:32 +0100462 int fd, rc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100463
Alexander Couzens841817e2020-11-19 00:41:29 +0100464 memset(&addr, 0, sizeof(addr));
465 addr.sll_family = AF_PACKET;
466 addr.sll_protocol = htons(ETH_P_ALL);
467 addr.sll_ifindex = ifindex;
468
Harald Welte5bea72e2020-12-10 22:06:21 +0100469 fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_HDLC));
Alexander Couzens841817e2020-11-19 00:41:29 +0100470 if (fd < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100471 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 +0100472 return fd;
473 }
474
Harald Welte41b188b2020-12-10 22:00:23 +0100475 /* there's a race condition between the above syscall and the bind() call below,
476 * causing other packets to be received in between */
477
Alexander Couzens841817e2020-11-19 00:41:29 +0100478 rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
479 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100480 LOGBIND(nsbind, LOGL_ERROR, "Can not bind AF_PACKET socket to ifindex %d\n", ifindex);
Alexander Couzens841817e2020-11-19 00:41:29 +0100481 close(fd);
482 return rc;
483 }
484
485 return fd;
486}
487
Harald Welte56f08a32020-12-01 23:07:32 +0100488#ifdef ENABLE_LIBMNL
489
490#include <osmocom/core/mnl.h>
Harald Welte56f08a32020-12-01 23:07:32 +0100491#include <linux/if_link.h>
492#include <linux/rtnetlink.h>
493
494#ifndef ARPHRD_FRAD
495#define ARPHRD_FRAD 770
496#endif
497
498/* validate the netlink attributes */
499static int data_attr_cb(const struct nlattr *attr, void *data)
500{
501 const struct nlattr **tb = data;
502 int type = mnl_attr_get_type(attr);
503
504 if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
505 return MNL_CB_OK;
506
507 switch (type) {
508 case IFLA_MTU:
509 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
510 return MNL_CB_ERROR;
511 break;
512 case IFLA_IFNAME:
513 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
514 return MNL_CB_ERROR;
515 break;
516 }
517 tb[type] = attr;
518 return MNL_CB_OK;
519}
520
521/* find the bind for the netdev (if any) */
522static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname)
523{
524 struct gprs_ns2_vc_bind *bind;
525
526 llist_for_each_entry(bind, &nsi->binding, list) {
527 struct priv_bind *bpriv = bind->priv;
528 if (!strcmp(bpriv->netif, ifname))
529 return bind;
530 }
531
532 return NULL;
533}
534
535/* handle a single netlink message received via libmnl */
536static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data)
537{
538 struct osmo_mnl *omnl = data;
539 struct gprs_ns2_vc_bind *bind;
540 struct nlattr *tb[IFLA_MAX+1] = {};
541 struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
542 struct gprs_ns2_inst *nsi;
543 const char *ifname;
544 bool if_running;
545
546 OSMO_ASSERT(omnl);
547 OSMO_ASSERT(ifm);
548
549 nsi = omnl->priv;
550
551 if (ifm->ifi_type != ARPHRD_FRAD)
552 return MNL_CB_OK;
553
554 mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
555
556 if (!tb[IFLA_IFNAME])
557 return MNL_CB_OK;
558 ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
559 if_running = !!(ifm->ifi_flags & IFF_RUNNING);
560
561 bind = bind4netdev(nsi, ifname);
562 if (bind) {
563 struct priv_bind *bpriv = bind->priv;
564 if (bpriv->if_running != if_running) {
565 /* update running state */
Harald Weltef2949742021-01-20 14:54:14 +0100566 LOGBIND(bind, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
567 ifname, if_running ? "UP" : "DOWN");
Harald Welte56f08a32020-12-01 23:07:32 +0100568 bpriv->if_running = if_running;
569 }
570 }
571
572 return MNL_CB_OK;
573}
574
575/* trigger one initial dump of all link information */
576static void linkmon_initial_dump(struct osmo_mnl *omnl)
577{
578 char buf[MNL_SOCKET_BUFFER_SIZE];
579 struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
580 struct rtgenmsg *rt;
581
582 nlh->nlmsg_type = RTM_GETLINK;
583 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
584 nlh->nlmsg_seq = time(NULL);
585 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
586 rt->rtgen_family = AF_PACKET;
587
588 if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
589 LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno));
590 }
591
592 /* the response[s] will be handled just like the events */
593}
594#endif /* LIBMNL */
595
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100596static int set_ifupdown(const char *netif, bool up)
597{
598 int sock, rc;
599 struct ifreq req;
600
601 sock = socket(AF_INET, SOCK_DGRAM, 0);
602 if (sock < 0)
603 return sock;
604
605 memset(&req, 0, sizeof req);
Harald Welte7f01b682020-12-21 12:39:38 +0100606 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100607
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100608 rc = ioctl(sock, SIOCGIFFLAGS, &req);
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100609 if (rc < 0) {
610 close(sock);
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100611 return rc;
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100612 }
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100613
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100614 if ((req.ifr_flags & IFF_UP) == up) {
615 close(sock);
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100616 return 0;
Vadim Yanitskiy222e8442021-01-05 14:33:29 +0100617 }
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100618
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100619 if (up)
620 req.ifr_flags |= IFF_UP;
621
622 rc = ioctl(sock, SIOCSIFFLAGS, &req);
623 close(sock);
624 return rc;
625}
626
Harald Weltef2949742021-01-20 14:54:14 +0100627static int setup_device(const char *netif, const struct gprs_ns2_vc_bind *bind)
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100628{
629 int sock, rc;
630 char buffer[128];
631 fr_proto *fr = (void*)buffer;
632 struct ifreq req;
633
634 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
635 if (sock < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100636 LOGBIND(bind, LOGL_ERROR, "%s: Unable to create socket: %s\n",
637 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100638 return sock;
639 }
640
641 memset(&req, 0, sizeof(struct ifreq));
642 memset(&buffer, 0, sizeof(buffer));
Harald Welteb8de1882020-12-21 12:40:45 +0100643 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100644 req.ifr_settings.ifs_ifsu.sync = (void*)buffer;
645 req.ifr_settings.size = sizeof(buffer);
646 req.ifr_settings.type = IF_GET_PROTO;
647
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100648 /* EINVAL is returned when no protocol has been set */
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100649 rc = ioctl(sock, SIOCWANDEV, &req);
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100650 if (rc < 0 && errno != EINVAL) {
Harald Weltef2949742021-01-20 14:54:14 +0100651 LOGBIND(bind, LOGL_ERROR, "%s: Unable to get FR protocol information: %s\n",
652 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100653 goto err;
654 }
655
656 /* check if the device is good */
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100657 if (rc == 0 && req.ifr_settings.type == IF_PROTO_FR && fr->lmi == LMI_NONE) {
Harald Weltef2949742021-01-20 14:54:14 +0100658 LOGBIND(bind, LOGL_NOTICE, "%s: has correct frame relay mode and lmi\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100659 goto ifup;
660 }
661
662 /* modify the device to match */
663 rc = set_ifupdown(netif, false);
664 if (rc) {
Harald Weltef2949742021-01-20 14:54:14 +0100665 LOGBIND(bind, LOGL_ERROR, "Unable to bring down the device %s: %s\n",
666 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100667 goto err;
668 }
669
670 memset(&req, 0, sizeof(struct ifreq));
671 memset(fr, 0, sizeof(fr_proto));
Harald Welteb8de1882020-12-21 12:40:45 +0100672 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100673 req.ifr_settings.type = IF_PROTO_FR;
674 req.ifr_settings.size = sizeof(fr_proto);
675 req.ifr_settings.ifs_ifsu.fr = fr;
676 fr->lmi = LMI_NONE;
677 /* even those settings aren't used, they must be in the range */
678 /* polling verification timer*/
679 fr->t391 = 10;
680 /* link integrity verification polling timer */
681 fr->t392 = 15;
682 /* full status polling counter*/
683 fr->n391 = 6;
684 /* error threshold */
685 fr->n392 = 3;
686 /* monitored events count */
687 fr->n393 = 4;
688
Harald Weltef2949742021-01-20 14:54:14 +0100689 LOGBIND(bind, LOGL_INFO, "%s: Setting frame relay related parameters\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100690 rc = ioctl(sock, SIOCWANDEV, &req);
691 if (rc) {
Harald Weltef2949742021-01-20 14:54:14 +0100692 LOGBIND(bind, LOGL_ERROR, "%s: Unable to set FR protocol on information: %s\n",
693 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100694 goto err;
695 }
696
697ifup:
698 rc = set_ifupdown(netif, true);
699 if (rc)
Harald Weltef2949742021-01-20 14:54:14 +0100700 LOGBIND(bind, LOGL_ERROR, "Unable to bring up the device %s: %s\n",
701 netif, strerror(errno));
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100702err:
703 close(sock);
704 return rc;
705}
Harald Welte56f08a32020-12-01 23:07:32 +0100706
Alexander Couzens841817e2020-11-19 00:41:29 +0100707/*! Create a new bind for NS over FR.
708 * \param[in] nsi NS instance in which to create the bind
709 * \param[in] netif Network interface to bind to
710 * \param[in] fr_network
711 * \param[in] fr_role
712 * \param[out] result pointer to created bind
713 * \return 0 on success; negative on error */
714int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100715 const char *name,
Alexander Couzens841817e2020-11-19 00:41:29 +0100716 const char *netif,
717 struct osmo_fr_network *fr_network,
718 enum osmo_fr_role fr_role,
719 struct gprs_ns2_vc_bind **result)
720{
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100721 struct gprs_ns2_vc_bind *bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100722 struct priv_bind *priv;
723 struct osmo_fr_link *fr_link;
724 int rc = 0;
725
Harald Weltec3aa8f92021-01-31 11:41:34 +0100726 if (strlen(netif) > IFNAMSIZ)
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100727 return -EINVAL;
728
729 if (gprs_ns2_bind_by_name(nsi, name))
730 return -EALREADY;
731
Harald Weltec3aa8f92021-01-31 11:41:34 +0100732 rc = ns2_bind_alloc(nsi, name, &bind);
733 if (rc < 0)
734 return rc;
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100735
Alexander Couzens841817e2020-11-19 00:41:29 +0100736 bind->driver = &vc_driver_fr;
Alexander Couzensaac90162020-11-19 02:44:04 +0100737 bind->ll = GPRS_NS2_LL_FR;
Alexander Couzens1c8785d2020-12-17 06:58:53 +0100738 /* 2 mbit */
739 bind->transfer_capability = 2;
Alexander Couzens841817e2020-11-19 00:41:29 +0100740 bind->send_vc = fr_vc_sendmsg;
741 bind->free_vc = free_vc;
742 bind->dump_vty = dump_vty;
Alexander Couzens841817e2020-11-19 00:41:29 +0100743 priv = bind->priv = talloc_zero(bind, struct priv_bind);
744 if (!priv) {
Harald Weltebdfb8b92021-01-31 11:44:57 +0100745 rc = -ENOMEM;
Harald Weltec3aa8f92021-01-31 11:41:34 +0100746 goto err_bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100747 }
748
Neels Hofmeyr8fef7612020-12-17 18:19:19 +0100749 OSMO_STRLCPY_ARRAY(priv->netif, netif);
Alexander Couzens841817e2020-11-19 00:41:29 +0100750
Alexander Couzens841817e2020-11-19 00:41:29 +0100751 /* FIXME: move fd handling into socket.c */
752 fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);
753 if (!fr_link) {
754 rc = -EINVAL;
Harald Weltec3aa8f92021-01-31 11:41:34 +0100755 goto err_bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100756 }
757
758 fr_link->tx_cb = fr_tx_cb;
Harald Welte2cc1d4d2021-01-31 18:33:31 +0100759 fr_link->cb_data = bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100760 priv->link = fr_link;
Harald Welte41b188b2020-12-10 22:00:23 +0100761
Alexander Couzens6f89c772020-12-17 03:06:39 +0100762 priv->ifindex = rc = devname2ifindex(netif);
763 if (rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100764 LOGBIND(bind, LOGL_ERROR, "Can not get interface index for interface %s\n", netif);
Harald Welte41b188b2020-12-10 22:00:23 +0100765 goto err_fr;
766 }
767
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100768 /* set protocol frame relay and lmi */
Harald Weltef2949742021-01-20 14:54:14 +0100769 rc = setup_device(priv->netif, bind);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100770 if(rc < 0) {
Harald Weltef2949742021-01-20 14:54:14 +0100771 LOGBIND(bind, LOGL_ERROR, "Failed to setup the interface %s for frame relay and lmi\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100772 goto err_fr;
773 }
774
Harald Weltef2949742021-01-20 14:54:14 +0100775 rc = open_socket(priv->ifindex, bind);
Alexander Couzens841817e2020-11-19 00:41:29 +0100776 if (rc < 0)
777 goto err_fr;
Harald Welted06128d2021-01-30 21:17:21 +0100778 INIT_LLIST_HEAD(&priv->backlog.list);
779 priv->backlog.retry_us = 2500; /* start with some non-zero value; this corrsponds to 496 bytes */
780 osmo_timer_setup(&priv->backlog.timer, fr_backlog_timer_cb, bind);
781 osmo_fd_setup(&priv->backlog.ofd, rc, OSMO_FD_READ, fr_netif_ofd_cb, bind, 0);
782 rc = osmo_fd_register(&priv->backlog.ofd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100783 if (rc < 0)
784 goto err_fd;
785
Harald Welte56f08a32020-12-01 23:07:32 +0100786#ifdef ENABLE_LIBMNL
787 if (!nsi->linkmon_mnl)
788 nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi);
789
790 /* we get a new full dump after every bind. which is a bit excessive. But that's just once
791 * at start-up, so we can get away with it */
792 if (nsi->linkmon_mnl)
793 linkmon_initial_dump(nsi->linkmon_mnl);
794#endif
795
Harald Weltec3aa8f92021-01-31 11:41:34 +0100796 if (result)
797 *result = bind;
798
Alexander Couzens841817e2020-11-19 00:41:29 +0100799 return rc;
800
801err_fd:
Harald Welted06128d2021-01-30 21:17:21 +0100802 close(priv->backlog.ofd.fd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100803err_fr:
804 osmo_fr_link_free(fr_link);
Alexander Couzens841817e2020-11-19 00:41:29 +0100805err_bind:
Harald Weltec3aa8f92021-01-31 11:41:34 +0100806 gprs_ns2_free_bind(bind);
Alexander Couzens841817e2020-11-19 00:41:29 +0100807
808 return rc;
809}
810
Alexander Couzensc782cec2020-12-10 04:10:25 +0100811/*! Return the frame relay role of a bind
812 * \param[in] bind The bind
813 * \return the frame relay role or -EINVAL if bind is not frame relay
814 */
815enum osmo_fr_role gprs_ns2_fr_bind_role(struct gprs_ns2_vc_bind *bind)
816{
817 struct priv_bind *priv;
818
819 if (bind->driver != &vc_driver_fr)
820 return -EINVAL;
821
822 priv = bind->priv;
823 return priv->link->role;
824}
825
Alexander Couzens841817e2020-11-19 00:41:29 +0100826/*! Return the network interface of the bind
827 * \param[in] bind The bind
828 * \return the network interface
829 */
830const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)
831{
832 struct priv_bind *priv;
833
834 if (bind->driver != &vc_driver_fr)
835 return NULL;
836
837 priv = bind->priv;
838 return priv->netif;
839}
840
841/*! Find NS bind for a given network interface
842 * \param[in] nsi NS instance
843 * \param[in] netif the network interface to search for
844 * \return the bind or NULL if not found
845 */
846struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
847 struct gprs_ns2_inst *nsi,
848 const char *netif)
849{
850 struct gprs_ns2_vc_bind *bind;
851 const char *_netif;
852
853 OSMO_ASSERT(nsi);
854 OSMO_ASSERT(netif);
855
856 llist_for_each_entry(bind, &nsi->binding, list) {
857 if (!gprs_ns2_is_fr_bind(bind))
858 continue;
859
860 _netif = gprs_ns2_fr_bind_netif(bind);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100861 if (!strncmp(_netif, netif, IFNAMSIZ))
Alexander Couzens841817e2020-11-19 00:41:29 +0100862 return bind;
863 }
864
865 return NULL;
866}
867
868/*! Create, connect and activate a new FR-based NS-VC
869 * \param[in] bind bind in which the new NS-VC is to be created
870 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
871 * \param[in] dlci Data Link connection identifier
872 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
873struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
Alexander Couzensebcbd722020-12-03 06:11:39 +0100874 struct gprs_ns2_nse *nse,
875 uint16_t nsvci,
876 uint16_t dlci)
877{
878 struct gprs_ns2_vc *nsvc = NULL;
879 struct priv_vc *priv = NULL;
Harald Welte603f4042020-11-29 17:39:19 +0100880 struct priv_bind *bpriv = bind->priv;
881 char idbuf[64];
Alexander Couzensebcbd722020-12-03 06:11:39 +0100882
Alexander Couzens55bc8692021-01-18 18:39:57 +0100883 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzensebcbd722020-12-03 06:11:39 +0100884 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
885 if (nsvc) {
886 goto err;
887 }
888
Harald Welte603f4042020-11-29 17:39:19 +0100889 snprintf(idbuf, sizeof(idbuf), "%s-%s-DLCI%u-NSE%05u-NSVC%05u", gprs_ns2_lltype_str(nse->ll),
890 bpriv->netif, dlci, nse->nsei, nsvci);
Alexander Couzens138b96f2021-01-25 16:23:29 +0100891 nsvc = ns2_vc_alloc(bind, nse, true, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
Alexander Couzensebcbd722020-12-03 06:11:39 +0100892 if (!nsvc)
893 goto err;
894
895 nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
896 if (!priv)
897 goto err;
898
899 nsvc->nsvci = nsvci;
900 nsvc->nsvci_is_valid = true;
901
Alexander Couzens8dfc24c2021-01-25 16:09:23 +0100902 ns2_vc_fsm_start(nsvc);
Alexander Couzensebcbd722020-12-03 06:11:39 +0100903
904 return nsvc;
905
906err:
907 gprs_ns2_free_nsvc(nsvc);
908 return NULL;
909}
910
911
912/*! Create, connect and activate a new FR-based NS-VC
913 * \param[in] bind bind in which the new NS-VC is to be created
914 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
915 * \param[in] dlci Data Link connection identifier
916 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
917struct gprs_ns2_vc *gprs_ns2_fr_connect2(struct gprs_ns2_vc_bind *bind,
Alexander Couzens841817e2020-11-19 00:41:29 +0100918 uint16_t nsei,
919 uint16_t nsvci,
920 uint16_t dlci)
921{
922 bool created_nse = false;
923 struct gprs_ns2_vc *nsvc = NULL;
Alexander Couzens55bc8692021-01-18 18:39:57 +0100924 struct gprs_ns2_nse *nse;
925
926 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
927 nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
Alexander Couzens841817e2020-11-19 00:41:29 +0100928 if (!nse) {
Alexander Couzens138b96f2021-01-25 16:23:29 +0100929 nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_FR, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
Alexander Couzens841817e2020-11-19 00:41:29 +0100930 if (!nse)
931 return NULL;
932 created_nse = true;
933 }
934
Harald Welte509047b2021-01-17 19:55:51 +0100935 nsvc = gprs_ns2_fr_connect(bind, nse, nsvci, dlci);
Alexander Couzens841817e2020-11-19 00:41:29 +0100936 if (!nsvc)
937 goto err_nse;
938
Alexander Couzens841817e2020-11-19 00:41:29 +0100939 return nsvc;
940
Alexander Couzens841817e2020-11-19 00:41:29 +0100941err_nse:
942 if (created_nse)
943 gprs_ns2_free_nse(nse);
944
945 return NULL;
946}
947
948/*! Return the nsvc by dlci.
949 * \param[in] bind
950 * \param[in] dlci Data Link connection identifier
951 * \return the nsvc or NULL if not found
952 */
953struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,
954 uint16_t dlci)
955{
956 struct gprs_ns2_vc *nsvc;
957 struct priv_vc *vcpriv;
958
Alexander Couzens55bc8692021-01-18 18:39:57 +0100959 OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
Alexander Couzens841817e2020-11-19 00:41:29 +0100960 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
961 vcpriv = nsvc->priv;
962
963 if (dlci == vcpriv->dlci)
964 return nsvc;
965 }
966
967 return NULL;
968}
969
970/*! Return the dlci of the nsvc
971 * \param[in] nsvc
972 * \return the dlci or 0 on error. 0 is not a valid dlci.
973 */
Alexander Couzens22c26e02020-12-10 04:10:07 +0100974uint16_t gprs_ns2_fr_nsvc_dlci(const struct gprs_ns2_vc *nsvc)
Alexander Couzens841817e2020-11-19 00:41:29 +0100975{
976 struct priv_vc *vcpriv;
977
978 if (!nsvc->bind)
979 return 0;
980
981 if (nsvc->bind->driver != &vc_driver_fr)
982 return 0;
983
984 vcpriv = nsvc->priv;
985 return vcpriv->dlci;
986}