blob: 5b54b48a5d989dace72f098c4031a52b0580d2d3 [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
7/* (C) 2009-2010,2014,2017 by Harald Welte <laforge@gnumonks.org>
8 * (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>
39#include <net/if.h>
40
41#include <sys/ioctl.h>
42#include <netpacket/packet.h>
43#include <linux/if_ether.h>
44#include <linux/hdlc.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010045
46#include <osmocom/gprs/frame_relay.h>
47#include <osmocom/core/byteswap.h>
48#include <osmocom/core/logging.h>
49#include <osmocom/core/msgb.h>
50#include <osmocom/core/select.h>
51#include <osmocom/core/socket.h>
52#include <osmocom/core/talloc.h>
Alexander Couzens60021a42020-12-17 02:48:22 +010053#include <osmocom/core/write_queue.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010054#include <osmocom/gprs/gprs_ns2.h>
55
Pau Espin Pedrold41800c2020-12-07 13:35:24 +010056#ifdef ENABLE_LIBMNL
57#include <osmocom/core/mnl.h>
58#endif
59
Harald Welte56f08a32020-12-01 23:07:32 +010060#include "config.h"
Alexander Couzens841817e2020-11-19 00:41:29 +010061#include "common_vty.h"
62#include "gprs_ns2_internal.h"
63
64#define GRE_PTYPE_FR 0x6559
65#define GRE_PTYPE_IPv4 0x0800
66#define GRE_PTYPE_IPv6 0x86dd
67#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
68
69#ifndef IPPROTO_GRE
70# define IPPROTO_GRE 47
71#endif
72
73struct gre_hdr {
74 uint16_t flags;
75 uint16_t ptype;
76} __attribute__ ((packed));
77
78static void free_bind(struct gprs_ns2_vc_bind *bind);
79static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg);
80
81struct gprs_ns2_vc_driver vc_driver_fr = {
82 .name = "GB frame relay",
83 .free_bind = free_bind,
84};
85
86struct priv_bind {
Alexander Couzens841817e2020-11-19 00:41:29 +010087 char netif[IF_NAMESIZE];
88 struct osmo_fr_link *link;
Alexander Couzens60021a42020-12-17 02:48:22 +010089 struct osmo_wqueue wqueue;
Harald Welte41b188b2020-12-10 22:00:23 +010090 int ifindex;
Harald Welte56f08a32020-12-01 23:07:32 +010091 bool if_running;
Alexander Couzens841817e2020-11-19 00:41:29 +010092};
93
94struct priv_vc {
95 struct osmo_sockaddr remote;
96 uint16_t dlci;
97 struct osmo_fr_dlc *dlc;
98};
99
100static void free_vc(struct gprs_ns2_vc *nsvc)
101{
102 OSMO_ASSERT(nsvc);
103
104 if (!nsvc->priv)
105 return;
106
107 talloc_free(nsvc->priv);
108 nsvc->priv = NULL;
109}
110
111static void dump_vty(const struct gprs_ns2_vc_bind *bind, struct vty *vty, bool _stats)
112{
113 struct priv_bind *priv;
114 struct gprs_ns2_vc *nsvc;
Harald Welte48bd76c2020-12-01 16:53:06 +0100115 struct osmo_fr_link *fr_link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100116
117 if (!bind)
118 return;
119
120 priv = bind->priv;
Harald Welte48bd76c2020-12-01 16:53:06 +0100121 fr_link = priv->link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100122
Harald Welte56f08a32020-12-01 23:07:32 +0100123 vty_out(vty, "FR bind: %s, role: %s, link: %s%s", priv->netif,
124 osmo_fr_role_str(fr_link->role), priv->if_running ? "UP" : "DOWN", VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100125
126 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
Harald Welte96ec84a2020-12-01 17:56:05 +0100127 vty_out(vty, " NSVCI %05u: %s%s", nsvc->nsvci, gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100128 }
129
130 priv = bind->priv;
131}
132
133/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
134static void free_bind(struct gprs_ns2_vc_bind *bind)
135{
136 struct priv_bind *priv;
137
138 if (!bind)
139 return;
140
141 priv = bind->priv;
142
143 OSMO_ASSERT(llist_empty(&bind->nsvc));
144
145 osmo_fr_link_free(priv->link);
Alexander Couzens60021a42020-12-17 02:48:22 +0100146 osmo_fd_close(&priv->wqueue.bfd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100147 talloc_free(priv);
148}
149
150static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,
151 struct gprs_ns2_vc *nsvc,
152 uint16_t dlci)
153{
154 struct priv_bind *privb = bind->priv;
155 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
156 if (!priv)
157 return NULL;
158
159 nsvc->priv = priv;
160 priv->dlci = dlci;
161 priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);
162 if (!priv->dlc) {
163 nsvc->priv = NULL;
164 talloc_free(priv);
165 return NULL;
166 }
167
168 priv->dlc->rx_cb_data = nsvc;
169 priv->dlc->rx_cb = fr_dlci_rx_cb;
170
171 return priv;
172}
173
174int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
175 uint16_t dlci,
176 struct gprs_ns2_vc **result)
177{
178 struct gprs_ns2_vc *nsvc;
179 struct priv_vc *vcpriv;
180
181 if (!result)
182 return -EINVAL;
183
184 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
185 vcpriv = nsvc->priv;
186 if (vcpriv->dlci != dlci) {
187 *result = nsvc;
188 return 0;
189 }
190 }
191
192 return 1;
193}
194
195/* PDU from the network interface towards the fr layer (upwards) */
196static int handle_netif_read(struct osmo_fd *bfd)
197{
198 struct gprs_ns2_vc_bind *bind = bfd->data;
199 struct priv_bind *priv = bind->priv;
200 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
Harald Welte41b188b2020-12-10 22:00:23 +0100201 struct sockaddr_ll sll;
202 socklen_t sll_len = sizeof(sll);
Alexander Couzens841817e2020-11-19 00:41:29 +0100203 int rc = 0;
204
205 if (!msg)
206 return -ENOMEM;
207
Harald Welte41b188b2020-12-10 22:00:23 +0100208 rc = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)&sll, &sll_len);
Alexander Couzens841817e2020-11-19 00:41:29 +0100209 if (rc < 0) {
210 LOGP(DLNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
211 strerror(errno));
212 goto out_err;
213 } else if (rc == 0) {
214 goto out_err;
215 }
216
Harald Welte41b188b2020-12-10 22:00:23 +0100217 /* ignore any packets that we might have received for a different interface, between
218 * the socket() and the bind() call */
219 if (sll.sll_ifindex != priv->ifindex)
220 goto out_err;
221
Alexander Couzens841817e2020-11-19 00:41:29 +0100222 msgb_put(msg, rc);
223 msg->dst = priv->link;
224 return osmo_fr_rx(msg);
225
226out_err:
227 msgb_free(msg);
228 return rc;
229}
230
231/* PDU from the frame relay towards the NS-VC (upwards) */
232static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)
233{
234 int rc;
235 struct gprs_ns2_vc *nsvc = cb_data;
236
237 rc = ns2_recv_vc(nsvc, msg);
238
239 return rc;
240}
241
Alexander Couzens60021a42020-12-17 02:48:22 +0100242static int handle_netif_write(struct osmo_fd *ofd, struct msgb *msg)
Alexander Couzens841817e2020-11-19 00:41:29 +0100243{
Alexander Couzens60021a42020-12-17 02:48:22 +0100244 return write(ofd->fd, msgb_data(msg), msgb_length(msg));
Alexander Couzens841817e2020-11-19 00:41:29 +0100245}
246
247/*! determine if given bind is for FR-GRE encapsulation. */
248int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
249{
250 return (bind->driver == &vc_driver_fr);
251}
252
253/* PDU from the NS-VC towards the frame relay layer (downwards) */
254static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
255{
256 struct priv_vc *vcpriv = nsvc->priv;
257
258 msg->dst = vcpriv->dlc;
259 return osmo_fr_tx_dlc(msg);
260}
261
262/* PDU from the frame relay layer towards the network interface (downwards) */
263int fr_tx_cb(void *data, struct msgb *msg)
264{
265 struct gprs_ns2_vc_bind *bind = data;
266 struct priv_bind *priv = bind->priv;
Alexander Couzens841817e2020-11-19 00:41:29 +0100267
Alexander Couzens60021a42020-12-17 02:48:22 +0100268 if (osmo_wqueue_enqueue(&priv->wqueue, msg)) {
269 LOGP(DLNS, LOGL_ERROR, "frame relay %s: failed to enqueue message\n",
270 priv->netif);
271 msgb_free(msg);
272 return -EINVAL;
273 }
Alexander Couzens841817e2020-11-19 00:41:29 +0100274
Alexander Couzens60021a42020-12-17 02:48:22 +0100275 return 0;
Alexander Couzens841817e2020-11-19 00:41:29 +0100276}
277
278static int devname2ifindex(const char *ifname)
279{
280 struct ifreq ifr;
281 int sk, rc;
282
283 sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
284 if (sk < 0)
285 return sk;
286
287
288 memset(&ifr, 0, sizeof(ifr));
Neels Hofmeyr475a0ac2020-12-17 18:10:34 +0100289 OSMO_STRLCPY_ARRAY(ifr.ifr_name, ifname);
Alexander Couzens841817e2020-11-19 00:41:29 +0100290
291 rc = ioctl(sk, SIOCGIFINDEX, &ifr);
292 close(sk);
293 if (rc < 0)
294 return rc;
295
296 return ifr.ifr_ifindex;
297}
298
Harald Welte41b188b2020-12-10 22:00:23 +0100299static int open_socket(int ifindex)
Alexander Couzens841817e2020-11-19 00:41:29 +0100300{
301 struct sockaddr_ll addr;
Harald Welte4ed0f4e2020-12-10 21:50:32 +0100302 int fd, rc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100303
Alexander Couzens841817e2020-11-19 00:41:29 +0100304 memset(&addr, 0, sizeof(addr));
305 addr.sll_family = AF_PACKET;
306 addr.sll_protocol = htons(ETH_P_ALL);
307 addr.sll_ifindex = ifindex;
308
Harald Welte5bea72e2020-12-10 22:06:21 +0100309 fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_HDLC));
Alexander Couzens841817e2020-11-19 00:41:29 +0100310 if (fd < 0) {
Harald Welte41b188b2020-12-10 22:00:23 +0100311 LOGP(DLNS, LOGL_ERROR, "Can not create AF_PACKET socket. Are you root or have CAP_RAW_SOCKET?\n");
Alexander Couzens841817e2020-11-19 00:41:29 +0100312 return fd;
313 }
314
Harald Welte41b188b2020-12-10 22:00:23 +0100315 /* there's a race condition between the above syscall and the bind() call below,
316 * causing other packets to be received in between */
317
Alexander Couzens841817e2020-11-19 00:41:29 +0100318 rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
319 if (rc < 0) {
Harald Welte41b188b2020-12-10 22:00:23 +0100320 LOGP(DLNS, LOGL_ERROR, "Can not bind AF_PACKET socket to ifindex %d\n", ifindex);
Alexander Couzens841817e2020-11-19 00:41:29 +0100321 close(fd);
322 return rc;
323 }
324
325 return fd;
326}
327
Harald Welte56f08a32020-12-01 23:07:32 +0100328#ifdef ENABLE_LIBMNL
329
330#include <osmocom/core/mnl.h>
Harald Welte56f08a32020-12-01 23:07:32 +0100331#include <linux/if_link.h>
332#include <linux/rtnetlink.h>
333
334#ifndef ARPHRD_FRAD
335#define ARPHRD_FRAD 770
336#endif
337
338/* validate the netlink attributes */
339static int data_attr_cb(const struct nlattr *attr, void *data)
340{
341 const struct nlattr **tb = data;
342 int type = mnl_attr_get_type(attr);
343
344 if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
345 return MNL_CB_OK;
346
347 switch (type) {
348 case IFLA_MTU:
349 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
350 return MNL_CB_ERROR;
351 break;
352 case IFLA_IFNAME:
353 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
354 return MNL_CB_ERROR;
355 break;
356 }
357 tb[type] = attr;
358 return MNL_CB_OK;
359}
360
361/* find the bind for the netdev (if any) */
362static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname)
363{
364 struct gprs_ns2_vc_bind *bind;
365
366 llist_for_each_entry(bind, &nsi->binding, list) {
367 struct priv_bind *bpriv = bind->priv;
368 if (!strcmp(bpriv->netif, ifname))
369 return bind;
370 }
371
372 return NULL;
373}
374
375/* handle a single netlink message received via libmnl */
376static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data)
377{
378 struct osmo_mnl *omnl = data;
379 struct gprs_ns2_vc_bind *bind;
380 struct nlattr *tb[IFLA_MAX+1] = {};
381 struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
382 struct gprs_ns2_inst *nsi;
383 const char *ifname;
384 bool if_running;
385
386 OSMO_ASSERT(omnl);
387 OSMO_ASSERT(ifm);
388
389 nsi = omnl->priv;
390
391 if (ifm->ifi_type != ARPHRD_FRAD)
392 return MNL_CB_OK;
393
394 mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
395
396 if (!tb[IFLA_IFNAME])
397 return MNL_CB_OK;
398 ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
399 if_running = !!(ifm->ifi_flags & IFF_RUNNING);
400
401 bind = bind4netdev(nsi, ifname);
402 if (bind) {
403 struct priv_bind *bpriv = bind->priv;
404 if (bpriv->if_running != if_running) {
405 /* update running state */
406 LOGP(DLNS, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
407 ifname, if_running ? "UP" : "DOWN");
408 bpriv->if_running = if_running;
409 }
410 }
411
412 return MNL_CB_OK;
413}
414
415/* trigger one initial dump of all link information */
416static void linkmon_initial_dump(struct osmo_mnl *omnl)
417{
418 char buf[MNL_SOCKET_BUFFER_SIZE];
419 struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
420 struct rtgenmsg *rt;
421
422 nlh->nlmsg_type = RTM_GETLINK;
423 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
424 nlh->nlmsg_seq = time(NULL);
425 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
426 rt->rtgen_family = AF_PACKET;
427
428 if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
429 LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno));
430 }
431
432 /* the response[s] will be handled just like the events */
433}
434#endif /* LIBMNL */
435
436
Alexander Couzens841817e2020-11-19 00:41:29 +0100437/*! Create a new bind for NS over FR.
438 * \param[in] nsi NS instance in which to create the bind
439 * \param[in] netif Network interface to bind to
440 * \param[in] fr_network
441 * \param[in] fr_role
442 * \param[out] result pointer to created bind
443 * \return 0 on success; negative on error */
444int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100445 const char *name,
Alexander Couzens841817e2020-11-19 00:41:29 +0100446 const char *netif,
447 struct osmo_fr_network *fr_network,
448 enum osmo_fr_role fr_role,
449 struct gprs_ns2_vc_bind **result)
450{
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100451 struct gprs_ns2_vc_bind *bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100452 struct priv_bind *priv;
453 struct osmo_fr_link *fr_link;
454 int rc = 0;
455
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100456 if (!name)
457 return -EINVAL;
458
459 if (gprs_ns2_bind_by_name(nsi, name))
460 return -EALREADY;
461
462 bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
Alexander Couzens841817e2020-11-19 00:41:29 +0100463 if (!bind)
464 return -ENOSPC;
465
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100466 bind->name = talloc_strdup(bind, name);
467 if (!bind->name) {
468 rc = -ENOSPC;
469 goto err_bind;
470 }
471
Alexander Couzens841817e2020-11-19 00:41:29 +0100472 bind->driver = &vc_driver_fr;
Alexander Couzensaac90162020-11-19 02:44:04 +0100473 bind->ll = GPRS_NS2_LL_FR;
Alexander Couzens841817e2020-11-19 00:41:29 +0100474 bind->send_vc = fr_vc_sendmsg;
475 bind->free_vc = free_vc;
476 bind->dump_vty = dump_vty;
477 bind->nsi = nsi;
478 priv = bind->priv = talloc_zero(bind, struct priv_bind);
479 if (!priv) {
480 rc = -ENOSPC;
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100481 goto err_name;
Alexander Couzens841817e2020-11-19 00:41:29 +0100482 }
483
Alexander Couzens841817e2020-11-19 00:41:29 +0100484 if (strlen(netif) > IF_NAMESIZE) {
485 rc = -EINVAL;
486 goto err_priv;
487 }
Neels Hofmeyr8fef7612020-12-17 18:19:19 +0100488 OSMO_STRLCPY_ARRAY(priv->netif, netif);
Alexander Couzens841817e2020-11-19 00:41:29 +0100489
Alexander Couzens841817e2020-11-19 00:41:29 +0100490 if (result)
491 *result = bind;
492
493 /* FIXME: move fd handling into socket.c */
494 fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);
495 if (!fr_link) {
496 rc = -EINVAL;
497 goto err_priv;
498 }
499
500 fr_link->tx_cb = fr_tx_cb;
501 fr_link->tx_cb_data = bind;
502 priv->link = fr_link;
Harald Welte41b188b2020-12-10 22:00:23 +0100503
Alexander Couzens6f89c772020-12-17 03:06:39 +0100504 priv->ifindex = rc = devname2ifindex(netif);
505 if (rc < 0) {
Harald Welte41b188b2020-12-10 22:00:23 +0100506 LOGP(DLNS, LOGL_ERROR, "Can not get interface index for interface %s\n", netif);
507 goto err_fr;
508 }
509
Alexander Couzens60021a42020-12-17 02:48:22 +0100510 rc = open_socket(priv->ifindex);
Alexander Couzens841817e2020-11-19 00:41:29 +0100511 if (rc < 0)
512 goto err_fr;
Alexander Couzens60021a42020-12-17 02:48:22 +0100513 osmo_wqueue_init(&priv->wqueue, 10);
514 priv->wqueue.write_cb = handle_netif_write;
515 priv->wqueue.read_cb = handle_netif_read;
516 osmo_fd_setup(&priv->wqueue.bfd, rc, OSMO_FD_READ, osmo_wqueue_bfd_cb, bind, 0);
517 rc = osmo_fd_register(&priv->wqueue.bfd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100518 if (rc < 0)
519 goto err_fd;
520
521 INIT_LLIST_HEAD(&bind->nsvc);
522 llist_add(&bind->list, &nsi->binding);
523
Harald Welte56f08a32020-12-01 23:07:32 +0100524#ifdef ENABLE_LIBMNL
525 if (!nsi->linkmon_mnl)
526 nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi);
527
528 /* we get a new full dump after every bind. which is a bit excessive. But that's just once
529 * at start-up, so we can get away with it */
530 if (nsi->linkmon_mnl)
531 linkmon_initial_dump(nsi->linkmon_mnl);
532#endif
533
Alexander Couzens841817e2020-11-19 00:41:29 +0100534 return rc;
535
536err_fd:
Alexander Couzens60021a42020-12-17 02:48:22 +0100537 close(priv->wqueue.bfd.fd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100538err_fr:
539 osmo_fr_link_free(fr_link);
540err_priv:
541 talloc_free(priv);
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100542err_name:
543 talloc_free((char *)bind->name);
Alexander Couzens841817e2020-11-19 00:41:29 +0100544err_bind:
545 talloc_free(bind);
546
547 return rc;
548}
549
Alexander Couzensc782cec2020-12-10 04:10:25 +0100550/*! Return the frame relay role of a bind
551 * \param[in] bind The bind
552 * \return the frame relay role or -EINVAL if bind is not frame relay
553 */
554enum osmo_fr_role gprs_ns2_fr_bind_role(struct gprs_ns2_vc_bind *bind)
555{
556 struct priv_bind *priv;
557
558 if (bind->driver != &vc_driver_fr)
559 return -EINVAL;
560
561 priv = bind->priv;
562 return priv->link->role;
563}
564
Alexander Couzens841817e2020-11-19 00:41:29 +0100565/*! Return the network interface of the bind
566 * \param[in] bind The bind
567 * \return the network interface
568 */
569const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)
570{
571 struct priv_bind *priv;
572
573 if (bind->driver != &vc_driver_fr)
574 return NULL;
575
576 priv = bind->priv;
577 return priv->netif;
578}
579
580/*! Find NS bind for a given network interface
581 * \param[in] nsi NS instance
582 * \param[in] netif the network interface to search for
583 * \return the bind or NULL if not found
584 */
585struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
586 struct gprs_ns2_inst *nsi,
587 const char *netif)
588{
589 struct gprs_ns2_vc_bind *bind;
590 const char *_netif;
591
592 OSMO_ASSERT(nsi);
593 OSMO_ASSERT(netif);
594
595 llist_for_each_entry(bind, &nsi->binding, list) {
596 if (!gprs_ns2_is_fr_bind(bind))
597 continue;
598
599 _netif = gprs_ns2_fr_bind_netif(bind);
600 if (!strncmp(_netif, netif, IF_NAMESIZE))
601 return bind;
602 }
603
604 return NULL;
605}
606
607/*! Create, connect and activate a new FR-based NS-VC
608 * \param[in] bind bind in which the new NS-VC is to be created
609 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
610 * \param[in] dlci Data Link connection identifier
611 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
612struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
Alexander Couzensebcbd722020-12-03 06:11:39 +0100613 struct gprs_ns2_nse *nse,
614 uint16_t nsvci,
615 uint16_t dlci)
616{
617 struct gprs_ns2_vc *nsvc = NULL;
618 struct priv_vc *priv = NULL;
619
620 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
621 if (nsvc) {
622 goto err;
623 }
624
625 nsvc = ns2_vc_alloc(bind, nse, true, NS2_VC_MODE_BLOCKRESET);
626 if (!nsvc)
627 goto err;
628
629 nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
630 if (!priv)
631 goto err;
632
633 nsvc->nsvci = nsvci;
634 nsvc->nsvci_is_valid = true;
635
636 gprs_ns2_vc_fsm_start(nsvc);
637
638 return nsvc;
639
640err:
641 gprs_ns2_free_nsvc(nsvc);
642 return NULL;
643}
644
645
646/*! Create, connect and activate a new FR-based NS-VC
647 * \param[in] bind bind in which the new NS-VC is to be created
648 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
649 * \param[in] dlci Data Link connection identifier
650 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
651struct gprs_ns2_vc *gprs_ns2_fr_connect2(struct gprs_ns2_vc_bind *bind,
Alexander Couzens841817e2020-11-19 00:41:29 +0100652 uint16_t nsei,
653 uint16_t nsvci,
654 uint16_t dlci)
655{
656 bool created_nse = false;
657 struct gprs_ns2_vc *nsvc = NULL;
658 struct priv_vc *priv = NULL;
659 struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
660 if (!nse) {
Alexander Couzensd923cff2020-12-01 01:03:52 +0100661 nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_FR, NS2_DIALECT_STATIC_RESETBLOCK);
Alexander Couzens841817e2020-11-19 00:41:29 +0100662 if (!nse)
663 return NULL;
664 created_nse = true;
665 }
666
667 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
668 if (nsvc) {
669 goto err_nse;
670 }
671
Alexander Couzensd923cff2020-12-01 01:03:52 +0100672 nsvc = ns2_vc_alloc(bind, nse, true, NS2_VC_MODE_BLOCKRESET);
Alexander Couzens841817e2020-11-19 00:41:29 +0100673 if (!nsvc)
674 goto err_nse;
675
676 nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
677 if (!priv)
678 goto err;
679
680 nsvc->nsvci = nsvci;
681 nsvc->nsvci_is_valid = true;
Alexander Couzens841817e2020-11-19 00:41:29 +0100682
683 gprs_ns2_vc_fsm_start(nsvc);
684
685 return nsvc;
686
687err:
688 gprs_ns2_free_nsvc(nsvc);
689err_nse:
690 if (created_nse)
691 gprs_ns2_free_nse(nse);
692
693 return NULL;
694}
695
696/*! Return the nsvc by dlci.
697 * \param[in] bind
698 * \param[in] dlci Data Link connection identifier
699 * \return the nsvc or NULL if not found
700 */
701struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,
702 uint16_t dlci)
703{
704 struct gprs_ns2_vc *nsvc;
705 struct priv_vc *vcpriv;
706
707 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
708 vcpriv = nsvc->priv;
709
710 if (dlci == vcpriv->dlci)
711 return nsvc;
712 }
713
714 return NULL;
715}
716
717/*! Return the dlci of the nsvc
718 * \param[in] nsvc
719 * \return the dlci or 0 on error. 0 is not a valid dlci.
720 */
Alexander Couzens22c26e02020-12-10 04:10:07 +0100721uint16_t gprs_ns2_fr_nsvc_dlci(const struct gprs_ns2_vc *nsvc)
Alexander Couzens841817e2020-11-19 00:41:29 +0100722{
723 struct priv_vc *vcpriv;
724
725 if (!nsvc->bind)
726 return 0;
727
728 if (nsvc->bind->driver != &vc_driver_fr)
729 return 0;
730
731 vcpriv = nsvc->priv;
732 return vcpriv->dlci;
733}