blob: e972a343fd025ead9ba306d0e21b092a9e986377 [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>
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>
50#include <osmocom/core/logging.h>
51#include <osmocom/core/msgb.h>
52#include <osmocom/core/select.h>
53#include <osmocom/core/socket.h>
54#include <osmocom/core/talloc.h>
Alexander Couzens60021a42020-12-17 02:48:22 +010055#include <osmocom/core/write_queue.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010056#include <osmocom/gprs/gprs_ns2.h>
57
Pau Espin Pedrold41800c2020-12-07 13:35:24 +010058#ifdef ENABLE_LIBMNL
59#include <osmocom/core/mnl.h>
60#endif
61
Harald Welte56f08a32020-12-01 23:07:32 +010062#include "config.h"
Alexander Couzens841817e2020-11-19 00:41:29 +010063#include "common_vty.h"
64#include "gprs_ns2_internal.h"
65
66#define GRE_PTYPE_FR 0x6559
67#define GRE_PTYPE_IPv4 0x0800
68#define GRE_PTYPE_IPv6 0x86dd
69#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
70
71#ifndef IPPROTO_GRE
72# define IPPROTO_GRE 47
73#endif
74
75struct gre_hdr {
76 uint16_t flags;
77 uint16_t ptype;
78} __attribute__ ((packed));
79
80static void free_bind(struct gprs_ns2_vc_bind *bind);
81static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg);
82
83struct gprs_ns2_vc_driver vc_driver_fr = {
84 .name = "GB frame relay",
85 .free_bind = free_bind,
86};
87
88struct priv_bind {
Alexander Couzens5c96f5d2020-12-17 04:45:03 +010089 char netif[IFNAMSIZ];
Alexander Couzens841817e2020-11-19 00:41:29 +010090 struct osmo_fr_link *link;
Alexander Couzens60021a42020-12-17 02:48:22 +010091 struct osmo_wqueue wqueue;
Harald Welte41b188b2020-12-10 22:00:23 +010092 int ifindex;
Harald Welte56f08a32020-12-01 23:07:32 +010093 bool if_running;
Alexander Couzens841817e2020-11-19 00:41:29 +010094};
95
96struct priv_vc {
97 struct osmo_sockaddr remote;
98 uint16_t dlci;
99 struct osmo_fr_dlc *dlc;
100};
101
102static void free_vc(struct gprs_ns2_vc *nsvc)
103{
104 OSMO_ASSERT(nsvc);
105
106 if (!nsvc->priv)
107 return;
108
109 talloc_free(nsvc->priv);
110 nsvc->priv = NULL;
111}
112
113static void dump_vty(const struct gprs_ns2_vc_bind *bind, struct vty *vty, bool _stats)
114{
115 struct priv_bind *priv;
116 struct gprs_ns2_vc *nsvc;
Harald Welte48bd76c2020-12-01 16:53:06 +0100117 struct osmo_fr_link *fr_link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100118
119 if (!bind)
120 return;
121
122 priv = bind->priv;
Harald Welte48bd76c2020-12-01 16:53:06 +0100123 fr_link = priv->link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100124
Harald Welte56f08a32020-12-01 23:07:32 +0100125 vty_out(vty, "FR bind: %s, role: %s, link: %s%s", priv->netif,
126 osmo_fr_role_str(fr_link->role), priv->if_running ? "UP" : "DOWN", VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100127
128 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
Harald Welte96ec84a2020-12-01 17:56:05 +0100129 vty_out(vty, " NSVCI %05u: %s%s", nsvc->nsvci, gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100130 }
131
132 priv = bind->priv;
133}
134
135/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
136static void free_bind(struct gprs_ns2_vc_bind *bind)
137{
138 struct priv_bind *priv;
139
140 if (!bind)
141 return;
142
143 priv = bind->priv;
144
145 OSMO_ASSERT(llist_empty(&bind->nsvc));
146
147 osmo_fr_link_free(priv->link);
Alexander Couzens60021a42020-12-17 02:48:22 +0100148 osmo_fd_close(&priv->wqueue.bfd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100149 talloc_free(priv);
150}
151
152static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,
153 struct gprs_ns2_vc *nsvc,
154 uint16_t dlci)
155{
156 struct priv_bind *privb = bind->priv;
157 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
158 if (!priv)
159 return NULL;
160
161 nsvc->priv = priv;
162 priv->dlci = dlci;
163 priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);
164 if (!priv->dlc) {
165 nsvc->priv = NULL;
166 talloc_free(priv);
167 return NULL;
168 }
169
170 priv->dlc->rx_cb_data = nsvc;
171 priv->dlc->rx_cb = fr_dlci_rx_cb;
172
173 return priv;
174}
175
176int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
177 uint16_t dlci,
178 struct gprs_ns2_vc **result)
179{
180 struct gprs_ns2_vc *nsvc;
181 struct priv_vc *vcpriv;
182
183 if (!result)
184 return -EINVAL;
185
186 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
187 vcpriv = nsvc->priv;
188 if (vcpriv->dlci != dlci) {
189 *result = nsvc;
190 return 0;
191 }
192 }
193
194 return 1;
195}
196
197/* PDU from the network interface towards the fr layer (upwards) */
198static int handle_netif_read(struct osmo_fd *bfd)
199{
200 struct gprs_ns2_vc_bind *bind = bfd->data;
201 struct priv_bind *priv = bind->priv;
202 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
Harald Welte41b188b2020-12-10 22:00:23 +0100203 struct sockaddr_ll sll;
204 socklen_t sll_len = sizeof(sll);
Alexander Couzens841817e2020-11-19 00:41:29 +0100205 int rc = 0;
206
207 if (!msg)
208 return -ENOMEM;
209
Harald Welte41b188b2020-12-10 22:00:23 +0100210 rc = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)&sll, &sll_len);
Alexander Couzens841817e2020-11-19 00:41:29 +0100211 if (rc < 0) {
212 LOGP(DLNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
213 strerror(errno));
214 goto out_err;
215 } else if (rc == 0) {
216 goto out_err;
217 }
218
Harald Welte41b188b2020-12-10 22:00:23 +0100219 /* ignore any packets that we might have received for a different interface, between
220 * the socket() and the bind() call */
221 if (sll.sll_ifindex != priv->ifindex)
222 goto out_err;
223
Alexander Couzens841817e2020-11-19 00:41:29 +0100224 msgb_put(msg, rc);
225 msg->dst = priv->link;
226 return osmo_fr_rx(msg);
227
228out_err:
229 msgb_free(msg);
230 return rc;
231}
232
233/* PDU from the frame relay towards the NS-VC (upwards) */
234static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)
235{
236 int rc;
237 struct gprs_ns2_vc *nsvc = cb_data;
238
239 rc = ns2_recv_vc(nsvc, msg);
240
241 return rc;
242}
243
Alexander Couzens60021a42020-12-17 02:48:22 +0100244static int handle_netif_write(struct osmo_fd *ofd, struct msgb *msg)
Alexander Couzens841817e2020-11-19 00:41:29 +0100245{
Alexander Couzens60021a42020-12-17 02:48:22 +0100246 return write(ofd->fd, msgb_data(msg), msgb_length(msg));
Alexander Couzens841817e2020-11-19 00:41:29 +0100247}
248
249/*! determine if given bind is for FR-GRE encapsulation. */
250int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
251{
252 return (bind->driver == &vc_driver_fr);
253}
254
255/* PDU from the NS-VC towards the frame relay layer (downwards) */
256static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
257{
258 struct priv_vc *vcpriv = nsvc->priv;
259
260 msg->dst = vcpriv->dlc;
261 return osmo_fr_tx_dlc(msg);
262}
263
264/* PDU from the frame relay layer towards the network interface (downwards) */
265int fr_tx_cb(void *data, struct msgb *msg)
266{
267 struct gprs_ns2_vc_bind *bind = data;
268 struct priv_bind *priv = bind->priv;
Alexander Couzens841817e2020-11-19 00:41:29 +0100269
Alexander Couzens60021a42020-12-17 02:48:22 +0100270 if (osmo_wqueue_enqueue(&priv->wqueue, msg)) {
271 LOGP(DLNS, LOGL_ERROR, "frame relay %s: failed to enqueue message\n",
272 priv->netif);
273 msgb_free(msg);
274 return -EINVAL;
275 }
Alexander Couzens841817e2020-11-19 00:41:29 +0100276
Alexander Couzens60021a42020-12-17 02:48:22 +0100277 return 0;
Alexander Couzens841817e2020-11-19 00:41:29 +0100278}
279
280static int devname2ifindex(const char *ifname)
281{
282 struct ifreq ifr;
283 int sk, rc;
284
285 sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
286 if (sk < 0)
287 return sk;
288
289
290 memset(&ifr, 0, sizeof(ifr));
Neels Hofmeyr475a0ac2020-12-17 18:10:34 +0100291 OSMO_STRLCPY_ARRAY(ifr.ifr_name, ifname);
Alexander Couzens841817e2020-11-19 00:41:29 +0100292
293 rc = ioctl(sk, SIOCGIFINDEX, &ifr);
294 close(sk);
295 if (rc < 0)
296 return rc;
297
298 return ifr.ifr_ifindex;
299}
300
Harald Welte41b188b2020-12-10 22:00:23 +0100301static int open_socket(int ifindex)
Alexander Couzens841817e2020-11-19 00:41:29 +0100302{
303 struct sockaddr_ll addr;
Harald Welte4ed0f4e2020-12-10 21:50:32 +0100304 int fd, rc;
Alexander Couzens841817e2020-11-19 00:41:29 +0100305
Alexander Couzens841817e2020-11-19 00:41:29 +0100306 memset(&addr, 0, sizeof(addr));
307 addr.sll_family = AF_PACKET;
308 addr.sll_protocol = htons(ETH_P_ALL);
309 addr.sll_ifindex = ifindex;
310
Harald Welte5bea72e2020-12-10 22:06:21 +0100311 fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_HDLC));
Alexander Couzens841817e2020-11-19 00:41:29 +0100312 if (fd < 0) {
Harald Welte41b188b2020-12-10 22:00:23 +0100313 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 +0100314 return fd;
315 }
316
Harald Welte41b188b2020-12-10 22:00:23 +0100317 /* there's a race condition between the above syscall and the bind() call below,
318 * causing other packets to be received in between */
319
Alexander Couzens841817e2020-11-19 00:41:29 +0100320 rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
321 if (rc < 0) {
Harald Welte41b188b2020-12-10 22:00:23 +0100322 LOGP(DLNS, LOGL_ERROR, "Can not bind AF_PACKET socket to ifindex %d\n", ifindex);
Alexander Couzens841817e2020-11-19 00:41:29 +0100323 close(fd);
324 return rc;
325 }
326
327 return fd;
328}
329
Harald Welte56f08a32020-12-01 23:07:32 +0100330#ifdef ENABLE_LIBMNL
331
332#include <osmocom/core/mnl.h>
Harald Welte56f08a32020-12-01 23:07:32 +0100333#include <linux/if_link.h>
334#include <linux/rtnetlink.h>
335
336#ifndef ARPHRD_FRAD
337#define ARPHRD_FRAD 770
338#endif
339
340/* validate the netlink attributes */
341static int data_attr_cb(const struct nlattr *attr, void *data)
342{
343 const struct nlattr **tb = data;
344 int type = mnl_attr_get_type(attr);
345
346 if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
347 return MNL_CB_OK;
348
349 switch (type) {
350 case IFLA_MTU:
351 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
352 return MNL_CB_ERROR;
353 break;
354 case IFLA_IFNAME:
355 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
356 return MNL_CB_ERROR;
357 break;
358 }
359 tb[type] = attr;
360 return MNL_CB_OK;
361}
362
363/* find the bind for the netdev (if any) */
364static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname)
365{
366 struct gprs_ns2_vc_bind *bind;
367
368 llist_for_each_entry(bind, &nsi->binding, list) {
369 struct priv_bind *bpriv = bind->priv;
370 if (!strcmp(bpriv->netif, ifname))
371 return bind;
372 }
373
374 return NULL;
375}
376
377/* handle a single netlink message received via libmnl */
378static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data)
379{
380 struct osmo_mnl *omnl = data;
381 struct gprs_ns2_vc_bind *bind;
382 struct nlattr *tb[IFLA_MAX+1] = {};
383 struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
384 struct gprs_ns2_inst *nsi;
385 const char *ifname;
386 bool if_running;
387
388 OSMO_ASSERT(omnl);
389 OSMO_ASSERT(ifm);
390
391 nsi = omnl->priv;
392
393 if (ifm->ifi_type != ARPHRD_FRAD)
394 return MNL_CB_OK;
395
396 mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
397
398 if (!tb[IFLA_IFNAME])
399 return MNL_CB_OK;
400 ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
401 if_running = !!(ifm->ifi_flags & IFF_RUNNING);
402
403 bind = bind4netdev(nsi, ifname);
404 if (bind) {
405 struct priv_bind *bpriv = bind->priv;
406 if (bpriv->if_running != if_running) {
407 /* update running state */
408 LOGP(DLNS, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
409 ifname, if_running ? "UP" : "DOWN");
410 bpriv->if_running = if_running;
411 }
412 }
413
414 return MNL_CB_OK;
415}
416
417/* trigger one initial dump of all link information */
418static void linkmon_initial_dump(struct osmo_mnl *omnl)
419{
420 char buf[MNL_SOCKET_BUFFER_SIZE];
421 struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
422 struct rtgenmsg *rt;
423
424 nlh->nlmsg_type = RTM_GETLINK;
425 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
426 nlh->nlmsg_seq = time(NULL);
427 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
428 rt->rtgen_family = AF_PACKET;
429
430 if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
431 LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno));
432 }
433
434 /* the response[s] will be handled just like the events */
435}
436#endif /* LIBMNL */
437
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100438static int set_ifupdown(const char *netif, bool up)
439{
440 int sock, rc;
441 struct ifreq req;
442
443 sock = socket(AF_INET, SOCK_DGRAM, 0);
444 if (sock < 0)
445 return sock;
446
447 memset(&req, 0, sizeof req);
Harald Welte7f01b682020-12-21 12:39:38 +0100448 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100449
Alexander Couzensfdea03b2020-12-29 23:13:23 +0100450 rc = ioctl(sock, SIOCGIFFLAGS, &req);
451 if (rc < 0)
452 return rc;
453
454 if ((req.ifr_flags & IFF_UP) == up)
455 return 0;
456
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100457 if (up)
458 req.ifr_flags |= IFF_UP;
459
460 rc = ioctl(sock, SIOCSIFFLAGS, &req);
461 close(sock);
462 return rc;
463}
464
465static int setup_device(const char *netif)
466{
467 int sock, rc;
468 char buffer[128];
469 fr_proto *fr = (void*)buffer;
470 struct ifreq req;
471
472 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
473 if (sock < 0) {
474 LOGP(DLNS, LOGL_ERROR, "%s: Unable to create socket: %s\n",
475 netif, strerror(errno));
476 return sock;
477 }
478
479 memset(&req, 0, sizeof(struct ifreq));
480 memset(&buffer, 0, sizeof(buffer));
Harald Welteb8de1882020-12-21 12:40:45 +0100481 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100482 req.ifr_settings.ifs_ifsu.sync = (void*)buffer;
483 req.ifr_settings.size = sizeof(buffer);
484 req.ifr_settings.type = IF_GET_PROTO;
485
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100486 /* EINVAL is returned when no protocol has been set */
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100487 rc = ioctl(sock, SIOCWANDEV, &req);
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100488 if (rc < 0 && errno != EINVAL) {
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100489 LOGP(DLNS, LOGL_ERROR, "%s: Unable to get FR protocol information: %s\n",
490 netif, strerror(errno));
491 goto err;
492 }
493
494 /* check if the device is good */
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100495 if (rc == 0 && req.ifr_settings.type == IF_PROTO_FR && fr->lmi == LMI_NONE) {
496 LOGP(DLNS, LOGL_NOTICE, "%s: has correct frame relay mode and lmi\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100497 goto ifup;
498 }
499
500 /* modify the device to match */
501 rc = set_ifupdown(netif, false);
502 if (rc) {
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100503 LOGP(DLNS, LOGL_ERROR, "Unable to bring down the device %s: %s\n",
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100504 netif, strerror(errno));
505 goto err;
506 }
507
508 memset(&req, 0, sizeof(struct ifreq));
509 memset(fr, 0, sizeof(fr_proto));
Harald Welteb8de1882020-12-21 12:40:45 +0100510 OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100511 req.ifr_settings.type = IF_PROTO_FR;
512 req.ifr_settings.size = sizeof(fr_proto);
513 req.ifr_settings.ifs_ifsu.fr = fr;
514 fr->lmi = LMI_NONE;
515 /* even those settings aren't used, they must be in the range */
516 /* polling verification timer*/
517 fr->t391 = 10;
518 /* link integrity verification polling timer */
519 fr->t392 = 15;
520 /* full status polling counter*/
521 fr->n391 = 6;
522 /* error threshold */
523 fr->n392 = 3;
524 /* monitored events count */
525 fr->n393 = 4;
526
Alexander Couzens8c33d4a2020-12-22 15:42:01 +0100527 LOGP(DLNS, LOGL_INFO, "%s: Setting frame relay related parameters\n", netif);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100528 rc = ioctl(sock, SIOCWANDEV, &req);
529 if (rc) {
530 LOGP(DLNS, LOGL_ERROR, "%s: Unable to set FR protocol on information: %s\n",
531 netif, strerror(errno));
532 goto err;
533 }
534
535ifup:
536 rc = set_ifupdown(netif, true);
537 if (rc)
538 LOGP(DLNS, LOGL_ERROR, "Unable to bring up the device %s: %s\n",
539 netif, strerror(errno));
540err:
541 close(sock);
542 return rc;
543}
Harald Welte56f08a32020-12-01 23:07:32 +0100544
Alexander Couzens841817e2020-11-19 00:41:29 +0100545/*! Create a new bind for NS over FR.
546 * \param[in] nsi NS instance in which to create the bind
547 * \param[in] netif Network interface to bind to
548 * \param[in] fr_network
549 * \param[in] fr_role
550 * \param[out] result pointer to created bind
551 * \return 0 on success; negative on error */
552int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100553 const char *name,
Alexander Couzens841817e2020-11-19 00:41:29 +0100554 const char *netif,
555 struct osmo_fr_network *fr_network,
556 enum osmo_fr_role fr_role,
557 struct gprs_ns2_vc_bind **result)
558{
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100559 struct gprs_ns2_vc_bind *bind;
Alexander Couzens841817e2020-11-19 00:41:29 +0100560 struct priv_bind *priv;
561 struct osmo_fr_link *fr_link;
562 int rc = 0;
563
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100564 if (!name)
565 return -EINVAL;
566
567 if (gprs_ns2_bind_by_name(nsi, name))
568 return -EALREADY;
569
570 bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
Alexander Couzens841817e2020-11-19 00:41:29 +0100571 if (!bind)
572 return -ENOSPC;
573
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100574 bind->name = talloc_strdup(bind, name);
575 if (!bind->name) {
576 rc = -ENOSPC;
577 goto err_bind;
578 }
579
Alexander Couzens841817e2020-11-19 00:41:29 +0100580 bind->driver = &vc_driver_fr;
Alexander Couzensaac90162020-11-19 02:44:04 +0100581 bind->ll = GPRS_NS2_LL_FR;
Alexander Couzens841817e2020-11-19 00:41:29 +0100582 bind->send_vc = fr_vc_sendmsg;
583 bind->free_vc = free_vc;
584 bind->dump_vty = dump_vty;
585 bind->nsi = nsi;
586 priv = bind->priv = talloc_zero(bind, struct priv_bind);
587 if (!priv) {
588 rc = -ENOSPC;
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100589 goto err_name;
Alexander Couzens841817e2020-11-19 00:41:29 +0100590 }
591
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100592 if (strlen(netif) > IFNAMSIZ) {
Alexander Couzens841817e2020-11-19 00:41:29 +0100593 rc = -EINVAL;
594 goto err_priv;
595 }
Neels Hofmeyr8fef7612020-12-17 18:19:19 +0100596 OSMO_STRLCPY_ARRAY(priv->netif, netif);
Alexander Couzens841817e2020-11-19 00:41:29 +0100597
Alexander Couzens841817e2020-11-19 00:41:29 +0100598 if (result)
599 *result = bind;
600
601 /* FIXME: move fd handling into socket.c */
602 fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);
603 if (!fr_link) {
604 rc = -EINVAL;
605 goto err_priv;
606 }
607
608 fr_link->tx_cb = fr_tx_cb;
609 fr_link->tx_cb_data = bind;
610 priv->link = fr_link;
Harald Welte41b188b2020-12-10 22:00:23 +0100611
Alexander Couzens6f89c772020-12-17 03:06:39 +0100612 priv->ifindex = rc = devname2ifindex(netif);
613 if (rc < 0) {
Harald Welte41b188b2020-12-10 22:00:23 +0100614 LOGP(DLNS, LOGL_ERROR, "Can not get interface index for interface %s\n", netif);
615 goto err_fr;
616 }
617
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100618 /* set protocol frame relay and lmi */
619 rc = setup_device(priv->netif);
620 if(rc < 0) {
621 LOGP(DLNS, LOGL_ERROR, "Failed to setup the interface %s for frame relay and lmi\n", netif);
622 goto err_fr;
623 }
624
Alexander Couzens60021a42020-12-17 02:48:22 +0100625 rc = open_socket(priv->ifindex);
Alexander Couzens841817e2020-11-19 00:41:29 +0100626 if (rc < 0)
627 goto err_fr;
Alexander Couzens60021a42020-12-17 02:48:22 +0100628 osmo_wqueue_init(&priv->wqueue, 10);
629 priv->wqueue.write_cb = handle_netif_write;
630 priv->wqueue.read_cb = handle_netif_read;
631 osmo_fd_setup(&priv->wqueue.bfd, rc, OSMO_FD_READ, osmo_wqueue_bfd_cb, bind, 0);
632 rc = osmo_fd_register(&priv->wqueue.bfd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100633 if (rc < 0)
634 goto err_fd;
635
636 INIT_LLIST_HEAD(&bind->nsvc);
637 llist_add(&bind->list, &nsi->binding);
638
Harald Welte56f08a32020-12-01 23:07:32 +0100639#ifdef ENABLE_LIBMNL
640 if (!nsi->linkmon_mnl)
641 nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi);
642
643 /* we get a new full dump after every bind. which is a bit excessive. But that's just once
644 * at start-up, so we can get away with it */
645 if (nsi->linkmon_mnl)
646 linkmon_initial_dump(nsi->linkmon_mnl);
647#endif
648
Alexander Couzens841817e2020-11-19 00:41:29 +0100649 return rc;
650
651err_fd:
Alexander Couzens60021a42020-12-17 02:48:22 +0100652 close(priv->wqueue.bfd.fd);
Alexander Couzens841817e2020-11-19 00:41:29 +0100653err_fr:
654 osmo_fr_link_free(fr_link);
655err_priv:
656 talloc_free(priv);
Alexander Couzensaaa55a62020-12-03 06:02:03 +0100657err_name:
658 talloc_free((char *)bind->name);
Alexander Couzens841817e2020-11-19 00:41:29 +0100659err_bind:
660 talloc_free(bind);
661
662 return rc;
663}
664
Alexander Couzensc782cec2020-12-10 04:10:25 +0100665/*! Return the frame relay role of a bind
666 * \param[in] bind The bind
667 * \return the frame relay role or -EINVAL if bind is not frame relay
668 */
669enum osmo_fr_role gprs_ns2_fr_bind_role(struct gprs_ns2_vc_bind *bind)
670{
671 struct priv_bind *priv;
672
673 if (bind->driver != &vc_driver_fr)
674 return -EINVAL;
675
676 priv = bind->priv;
677 return priv->link->role;
678}
679
Alexander Couzens841817e2020-11-19 00:41:29 +0100680/*! Return the network interface of the bind
681 * \param[in] bind The bind
682 * \return the network interface
683 */
684const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)
685{
686 struct priv_bind *priv;
687
688 if (bind->driver != &vc_driver_fr)
689 return NULL;
690
691 priv = bind->priv;
692 return priv->netif;
693}
694
695/*! Find NS bind for a given network interface
696 * \param[in] nsi NS instance
697 * \param[in] netif the network interface to search for
698 * \return the bind or NULL if not found
699 */
700struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
701 struct gprs_ns2_inst *nsi,
702 const char *netif)
703{
704 struct gprs_ns2_vc_bind *bind;
705 const char *_netif;
706
707 OSMO_ASSERT(nsi);
708 OSMO_ASSERT(netif);
709
710 llist_for_each_entry(bind, &nsi->binding, list) {
711 if (!gprs_ns2_is_fr_bind(bind))
712 continue;
713
714 _netif = gprs_ns2_fr_bind_netif(bind);
Alexander Couzens5c96f5d2020-12-17 04:45:03 +0100715 if (!strncmp(_netif, netif, IFNAMSIZ))
Alexander Couzens841817e2020-11-19 00:41:29 +0100716 return bind;
717 }
718
719 return NULL;
720}
721
722/*! Create, connect and activate a new FR-based NS-VC
723 * \param[in] bind bind in which the new NS-VC is to be created
724 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
725 * \param[in] dlci Data Link connection identifier
726 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
727struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
Alexander Couzensebcbd722020-12-03 06:11:39 +0100728 struct gprs_ns2_nse *nse,
729 uint16_t nsvci,
730 uint16_t dlci)
731{
732 struct gprs_ns2_vc *nsvc = NULL;
733 struct priv_vc *priv = NULL;
734
735 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
736 if (nsvc) {
737 goto err;
738 }
739
740 nsvc = ns2_vc_alloc(bind, nse, true, NS2_VC_MODE_BLOCKRESET);
741 if (!nsvc)
742 goto err;
743
744 nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
745 if (!priv)
746 goto err;
747
748 nsvc->nsvci = nsvci;
749 nsvc->nsvci_is_valid = true;
750
751 gprs_ns2_vc_fsm_start(nsvc);
752
753 return nsvc;
754
755err:
756 gprs_ns2_free_nsvc(nsvc);
757 return NULL;
758}
759
760
761/*! Create, connect and activate a new FR-based NS-VC
762 * \param[in] bind bind in which the new NS-VC is to be created
763 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
764 * \param[in] dlci Data Link connection identifier
765 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
766struct gprs_ns2_vc *gprs_ns2_fr_connect2(struct gprs_ns2_vc_bind *bind,
Alexander Couzens841817e2020-11-19 00:41:29 +0100767 uint16_t nsei,
768 uint16_t nsvci,
769 uint16_t dlci)
770{
771 bool created_nse = false;
772 struct gprs_ns2_vc *nsvc = NULL;
773 struct priv_vc *priv = NULL;
774 struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
775 if (!nse) {
Alexander Couzensd923cff2020-12-01 01:03:52 +0100776 nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_FR, NS2_DIALECT_STATIC_RESETBLOCK);
Alexander Couzens841817e2020-11-19 00:41:29 +0100777 if (!nse)
778 return NULL;
779 created_nse = true;
780 }
781
782 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
783 if (nsvc) {
784 goto err_nse;
785 }
786
Alexander Couzensd923cff2020-12-01 01:03:52 +0100787 nsvc = ns2_vc_alloc(bind, nse, true, NS2_VC_MODE_BLOCKRESET);
Alexander Couzens841817e2020-11-19 00:41:29 +0100788 if (!nsvc)
789 goto err_nse;
790
791 nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
792 if (!priv)
793 goto err;
794
795 nsvc->nsvci = nsvci;
796 nsvc->nsvci_is_valid = true;
Alexander Couzens841817e2020-11-19 00:41:29 +0100797
798 gprs_ns2_vc_fsm_start(nsvc);
799
800 return nsvc;
801
802err:
803 gprs_ns2_free_nsvc(nsvc);
804err_nse:
805 if (created_nse)
806 gprs_ns2_free_nse(nse);
807
808 return NULL;
809}
810
811/*! Return the nsvc by dlci.
812 * \param[in] bind
813 * \param[in] dlci Data Link connection identifier
814 * \return the nsvc or NULL if not found
815 */
816struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,
817 uint16_t dlci)
818{
819 struct gprs_ns2_vc *nsvc;
820 struct priv_vc *vcpriv;
821
822 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
823 vcpriv = nsvc->priv;
824
825 if (dlci == vcpriv->dlci)
826 return nsvc;
827 }
828
829 return NULL;
830}
831
832/*! Return the dlci of the nsvc
833 * \param[in] nsvc
834 * \return the dlci or 0 on error. 0 is not a valid dlci.
835 */
Alexander Couzens22c26e02020-12-10 04:10:07 +0100836uint16_t gprs_ns2_fr_nsvc_dlci(const struct gprs_ns2_vc *nsvc)
Alexander Couzens841817e2020-11-19 00:41:29 +0100837{
838 struct priv_vc *vcpriv;
839
840 if (!nsvc->bind)
841 return 0;
842
843 if (nsvc->bind->driver != &vc_driver_fr)
844 return 0;
845
846 vcpriv = nsvc->priv;
847 return vcpriv->dlci;
848}