blob: a651e1d4300b4d3f8f75f076e93e11eda3f4a5dc [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>
Harald Welte56f08a32020-12-01 23:07:32 +010053#include <osmocom/core/mnl.h>
Alexander Couzens841817e2020-11-19 00:41:29 +010054#include <osmocom/gprs/gprs_ns2.h>
55
Harald Welte56f08a32020-12-01 23:07:32 +010056#include "config.h"
Alexander Couzens841817e2020-11-19 00:41:29 +010057#include "common_vty.h"
58#include "gprs_ns2_internal.h"
59
60#define GRE_PTYPE_FR 0x6559
61#define GRE_PTYPE_IPv4 0x0800
62#define GRE_PTYPE_IPv6 0x86dd
63#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
64
65#ifndef IPPROTO_GRE
66# define IPPROTO_GRE 47
67#endif
68
69struct gre_hdr {
70 uint16_t flags;
71 uint16_t ptype;
72} __attribute__ ((packed));
73
74static void free_bind(struct gprs_ns2_vc_bind *bind);
75static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg);
76
77struct gprs_ns2_vc_driver vc_driver_fr = {
78 .name = "GB frame relay",
79 .free_bind = free_bind,
80};
81
82struct priv_bind {
83 struct osmo_fd fd;
84 char netif[IF_NAMESIZE];
85 struct osmo_fr_link *link;
Harald Welte56f08a32020-12-01 23:07:32 +010086 bool if_running;
Alexander Couzens841817e2020-11-19 00:41:29 +010087};
88
89struct priv_vc {
90 struct osmo_sockaddr remote;
91 uint16_t dlci;
92 struct osmo_fr_dlc *dlc;
93};
94
95static void free_vc(struct gprs_ns2_vc *nsvc)
96{
97 OSMO_ASSERT(nsvc);
98
99 if (!nsvc->priv)
100 return;
101
102 talloc_free(nsvc->priv);
103 nsvc->priv = NULL;
104}
105
106static void dump_vty(const struct gprs_ns2_vc_bind *bind, struct vty *vty, bool _stats)
107{
108 struct priv_bind *priv;
109 struct gprs_ns2_vc *nsvc;
Harald Welte48bd76c2020-12-01 16:53:06 +0100110 struct osmo_fr_link *fr_link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100111
112 if (!bind)
113 return;
114
115 priv = bind->priv;
Harald Welte48bd76c2020-12-01 16:53:06 +0100116 fr_link = priv->link;
Alexander Couzens841817e2020-11-19 00:41:29 +0100117
Harald Welte56f08a32020-12-01 23:07:32 +0100118 vty_out(vty, "FR bind: %s, role: %s, link: %s%s", priv->netif,
119 osmo_fr_role_str(fr_link->role), priv->if_running ? "UP" : "DOWN", VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100120
121 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
Harald Welte96ec84a2020-12-01 17:56:05 +0100122 vty_out(vty, " NSVCI %05u: %s%s", nsvc->nsvci, gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
Alexander Couzens841817e2020-11-19 00:41:29 +0100123 }
124
125 priv = bind->priv;
126}
127
128/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
129static void free_bind(struct gprs_ns2_vc_bind *bind)
130{
131 struct priv_bind *priv;
132
133 if (!bind)
134 return;
135
136 priv = bind->priv;
137
138 OSMO_ASSERT(llist_empty(&bind->nsvc));
139
140 osmo_fr_link_free(priv->link);
141 osmo_fd_close(&priv->fd);
142 talloc_free(priv);
143}
144
145static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,
146 struct gprs_ns2_vc *nsvc,
147 uint16_t dlci)
148{
149 struct priv_bind *privb = bind->priv;
150 struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
151 if (!priv)
152 return NULL;
153
154 nsvc->priv = priv;
155 priv->dlci = dlci;
156 priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);
157 if (!priv->dlc) {
158 nsvc->priv = NULL;
159 talloc_free(priv);
160 return NULL;
161 }
162
163 priv->dlc->rx_cb_data = nsvc;
164 priv->dlc->rx_cb = fr_dlci_rx_cb;
165
166 return priv;
167}
168
169int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
170 uint16_t dlci,
171 struct gprs_ns2_vc **result)
172{
173 struct gprs_ns2_vc *nsvc;
174 struct priv_vc *vcpriv;
175
176 if (!result)
177 return -EINVAL;
178
179 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
180 vcpriv = nsvc->priv;
181 if (vcpriv->dlci != dlci) {
182 *result = nsvc;
183 return 0;
184 }
185 }
186
187 return 1;
188}
189
190/* PDU from the network interface towards the fr layer (upwards) */
191static int handle_netif_read(struct osmo_fd *bfd)
192{
193 struct gprs_ns2_vc_bind *bind = bfd->data;
194 struct priv_bind *priv = bind->priv;
195 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
196 int rc = 0;
197
198 if (!msg)
199 return -ENOMEM;
200
201 rc = read(bfd->fd, msg->data, NS_ALLOC_SIZE);
202 if (rc < 0) {
203 LOGP(DLNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
204 strerror(errno));
205 goto out_err;
206 } else if (rc == 0) {
207 goto out_err;
208 }
209
210 msgb_put(msg, rc);
211 msg->dst = priv->link;
212 return osmo_fr_rx(msg);
213
214out_err:
215 msgb_free(msg);
216 return rc;
217}
218
219/* PDU from the frame relay towards the NS-VC (upwards) */
220static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)
221{
222 int rc;
223 struct gprs_ns2_vc *nsvc = cb_data;
224
225 rc = ns2_recv_vc(nsvc, msg);
226
227 return rc;
228}
229
230static int handle_netif_write(struct osmo_fd *bfd)
231{
232 /* FIXME */
233 return -EIO;
234}
235
236static int fr_fd_cb(struct osmo_fd *bfd, unsigned int what)
237{
238 int rc = 0;
239
240 if (what & OSMO_FD_READ)
241 rc = handle_netif_read(bfd);
242 if (what & OSMO_FD_WRITE)
243 rc = handle_netif_write(bfd);
244
245 return rc;
246}
247
248/*! determine if given bind is for FR-GRE encapsulation. */
249int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
250{
251 return (bind->driver == &vc_driver_fr);
252}
253
254/* PDU from the NS-VC towards the frame relay layer (downwards) */
255static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
256{
257 struct priv_vc *vcpriv = nsvc->priv;
258
259 msg->dst = vcpriv->dlc;
260 return osmo_fr_tx_dlc(msg);
261}
262
263/* PDU from the frame relay layer towards the network interface (downwards) */
264int fr_tx_cb(void *data, struct msgb *msg)
265{
266 struct gprs_ns2_vc_bind *bind = data;
267 struct priv_bind *priv = bind->priv;
268 int rc;
269
270 /* FIXME half writes */
271 rc = write(priv->fd.fd, msg->data, msg->len);
272 msgb_free(msg);
273
274 return rc;
275}
276
277static int devname2ifindex(const char *ifname)
278{
279 struct ifreq ifr;
280 int sk, rc;
281
282 sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
283 if (sk < 0)
284 return sk;
285
286
287 memset(&ifr, 0, sizeof(ifr));
288 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
289 ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0;
290
291 rc = ioctl(sk, SIOCGIFINDEX, &ifr);
292 close(sk);
293 if (rc < 0)
294 return rc;
295
296 return ifr.ifr_ifindex;
297}
298
299static int open_socket(const char *ifname)
300{
301 struct sockaddr_ll addr;
302 int ifindex;
303 int fd, rc, on = 1;
304
305 ifindex = devname2ifindex(ifname);
306 if (ifindex < 0) {
307 LOGP(DLNS, LOGL_ERROR, "Can not get interface index for interface %s\n", ifname);
308 return ifindex;
309 }
310
311 memset(&addr, 0, sizeof(addr));
312 addr.sll_family = AF_PACKET;
313 addr.sll_protocol = htons(ETH_P_ALL);
314 addr.sll_ifindex = ifindex;
315
316 fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
317 if (fd < 0) {
318 LOGP(DLNS, LOGL_ERROR, "Can not get socket for interface %s. Are you root or have CAP_RAW_SOCKET?\n", ifname);
319 return fd;
320 }
321
322 if (ioctl(fd, FIONBIO, (unsigned char *)&on) < 0) {
323 LOGP(DLGLOBAL, LOGL_ERROR,
324 "cannot set this socket unblocking: %s\n",
325 strerror(errno));
326 close(fd);
Harald Welteb6b82da2020-11-26 09:30:37 +0100327 return -EINVAL;
Alexander Couzens841817e2020-11-19 00:41:29 +0100328 }
329
330 rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
331 if (rc < 0) {
332 LOGP(DLNS, LOGL_ERROR, "Can not bind for interface %s\n", ifname);
333 close(fd);
334 return rc;
335 }
336
337 return fd;
338}
339
Harald Welte56f08a32020-12-01 23:07:32 +0100340#ifdef ENABLE_LIBMNL
341
342#include <osmocom/core/mnl.h>
Harald Welte56f08a32020-12-01 23:07:32 +0100343#include <linux/if_link.h>
344#include <linux/rtnetlink.h>
345
346#ifndef ARPHRD_FRAD
347#define ARPHRD_FRAD 770
348#endif
349
350/* validate the netlink attributes */
351static int data_attr_cb(const struct nlattr *attr, void *data)
352{
353 const struct nlattr **tb = data;
354 int type = mnl_attr_get_type(attr);
355
356 if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
357 return MNL_CB_OK;
358
359 switch (type) {
360 case IFLA_MTU:
361 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
362 return MNL_CB_ERROR;
363 break;
364 case IFLA_IFNAME:
365 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
366 return MNL_CB_ERROR;
367 break;
368 }
369 tb[type] = attr;
370 return MNL_CB_OK;
371}
372
373/* find the bind for the netdev (if any) */
374static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname)
375{
376 struct gprs_ns2_vc_bind *bind;
377
378 llist_for_each_entry(bind, &nsi->binding, list) {
379 struct priv_bind *bpriv = bind->priv;
380 if (!strcmp(bpriv->netif, ifname))
381 return bind;
382 }
383
384 return NULL;
385}
386
387/* handle a single netlink message received via libmnl */
388static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data)
389{
390 struct osmo_mnl *omnl = data;
391 struct gprs_ns2_vc_bind *bind;
392 struct nlattr *tb[IFLA_MAX+1] = {};
393 struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
394 struct gprs_ns2_inst *nsi;
395 const char *ifname;
396 bool if_running;
397
398 OSMO_ASSERT(omnl);
399 OSMO_ASSERT(ifm);
400
401 nsi = omnl->priv;
402
403 if (ifm->ifi_type != ARPHRD_FRAD)
404 return MNL_CB_OK;
405
406 mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
407
408 if (!tb[IFLA_IFNAME])
409 return MNL_CB_OK;
410 ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
411 if_running = !!(ifm->ifi_flags & IFF_RUNNING);
412
413 bind = bind4netdev(nsi, ifname);
414 if (bind) {
415 struct priv_bind *bpriv = bind->priv;
416 if (bpriv->if_running != if_running) {
417 /* update running state */
418 LOGP(DLNS, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
419 ifname, if_running ? "UP" : "DOWN");
420 bpriv->if_running = if_running;
421 }
422 }
423
424 return MNL_CB_OK;
425}
426
427/* trigger one initial dump of all link information */
428static void linkmon_initial_dump(struct osmo_mnl *omnl)
429{
430 char buf[MNL_SOCKET_BUFFER_SIZE];
431 struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
432 struct rtgenmsg *rt;
433
434 nlh->nlmsg_type = RTM_GETLINK;
435 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
436 nlh->nlmsg_seq = time(NULL);
437 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
438 rt->rtgen_family = AF_PACKET;
439
440 if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
441 LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno));
442 }
443
444 /* the response[s] will be handled just like the events */
445}
446#endif /* LIBMNL */
447
448
Alexander Couzens841817e2020-11-19 00:41:29 +0100449/*! Create a new bind for NS over FR.
450 * \param[in] nsi NS instance in which to create the bind
451 * \param[in] netif Network interface to bind to
452 * \param[in] fr_network
453 * \param[in] fr_role
454 * \param[out] result pointer to created bind
455 * \return 0 on success; negative on error */
456int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
457 const char *netif,
458 struct osmo_fr_network *fr_network,
459 enum osmo_fr_role fr_role,
460 struct gprs_ns2_vc_bind **result)
461{
462 struct gprs_ns2_vc_bind *bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
463 struct priv_bind *priv;
464 struct osmo_fr_link *fr_link;
465 int rc = 0;
466
467 if (!bind)
468 return -ENOSPC;
469
470 bind->driver = &vc_driver_fr;
Alexander Couzensaac90162020-11-19 02:44:04 +0100471 bind->ll = GPRS_NS2_LL_FR;
Alexander Couzens841817e2020-11-19 00:41:29 +0100472 bind->send_vc = fr_vc_sendmsg;
473 bind->free_vc = free_vc;
474 bind->dump_vty = dump_vty;
475 bind->nsi = nsi;
476 priv = bind->priv = talloc_zero(bind, struct priv_bind);
477 if (!priv) {
478 rc = -ENOSPC;
479 goto err_bind;
480 }
481
482 priv->fd.cb = fr_fd_cb;
483 priv->fd.data = bind;
484 if (strlen(netif) > IF_NAMESIZE) {
485 rc = -EINVAL;
486 goto err_priv;
487 }
488 strncpy(priv->netif, netif, sizeof(priv->netif));
489
490 ns2_vty_bind_apply(bind);
491 if (result)
492 *result = bind;
493
494 /* FIXME: move fd handling into socket.c */
495 fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);
496 if (!fr_link) {
497 rc = -EINVAL;
498 goto err_priv;
499 }
500
501 fr_link->tx_cb = fr_tx_cb;
502 fr_link->tx_cb_data = bind;
503 priv->link = fr_link;
504 priv->fd.fd = rc = open_socket(netif);
505 if (rc < 0)
506 goto err_fr;
507
508 priv->fd.when = OSMO_FD_READ;
509 rc = osmo_fd_register(&priv->fd);
510 if (rc < 0)
511 goto err_fd;
512
513 INIT_LLIST_HEAD(&bind->nsvc);
514 llist_add(&bind->list, &nsi->binding);
515
Harald Welte56f08a32020-12-01 23:07:32 +0100516#ifdef ENABLE_LIBMNL
517 if (!nsi->linkmon_mnl)
518 nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi);
519
520 /* we get a new full dump after every bind. which is a bit excessive. But that's just once
521 * at start-up, so we can get away with it */
522 if (nsi->linkmon_mnl)
523 linkmon_initial_dump(nsi->linkmon_mnl);
524#endif
525
Alexander Couzens841817e2020-11-19 00:41:29 +0100526 return rc;
527
528err_fd:
529 close(priv->fd.fd);
530err_fr:
531 osmo_fr_link_free(fr_link);
532err_priv:
533 talloc_free(priv);
534err_bind:
535 talloc_free(bind);
536
537 return rc;
538}
539
540/*! Return the network interface of the bind
541 * \param[in] bind The bind
542 * \return the network interface
543 */
544const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)
545{
546 struct priv_bind *priv;
547
548 if (bind->driver != &vc_driver_fr)
549 return NULL;
550
551 priv = bind->priv;
552 return priv->netif;
553}
554
555/*! Find NS bind for a given network interface
556 * \param[in] nsi NS instance
557 * \param[in] netif the network interface to search for
558 * \return the bind or NULL if not found
559 */
560struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
561 struct gprs_ns2_inst *nsi,
562 const char *netif)
563{
564 struct gprs_ns2_vc_bind *bind;
565 const char *_netif;
566
567 OSMO_ASSERT(nsi);
568 OSMO_ASSERT(netif);
569
570 llist_for_each_entry(bind, &nsi->binding, list) {
571 if (!gprs_ns2_is_fr_bind(bind))
572 continue;
573
574 _netif = gprs_ns2_fr_bind_netif(bind);
575 if (!strncmp(_netif, netif, IF_NAMESIZE))
576 return bind;
577 }
578
579 return NULL;
580}
581
582/*! Create, connect and activate a new FR-based NS-VC
583 * \param[in] bind bind in which the new NS-VC is to be created
584 * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
585 * \param[in] dlci Data Link connection identifier
586 * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
587struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
588 uint16_t nsei,
589 uint16_t nsvci,
590 uint16_t dlci)
591{
592 bool created_nse = false;
593 struct gprs_ns2_vc *nsvc = NULL;
594 struct priv_vc *priv = NULL;
595 struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
596 if (!nse) {
Alexander Couzensaac90162020-11-19 02:44:04 +0100597 nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_FR);
Alexander Couzens841817e2020-11-19 00:41:29 +0100598 if (!nse)
599 return NULL;
600 created_nse = true;
601 }
602
603 nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
604 if (nsvc) {
605 goto err_nse;
606 }
607
608 nsvc = ns2_vc_alloc(bind, nse, true);
609 if (!nsvc)
610 goto err_nse;
611
612 nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
613 if (!priv)
614 goto err;
615
616 nsvc->nsvci = nsvci;
617 nsvc->nsvci_is_valid = true;
Alexander Couzens841817e2020-11-19 00:41:29 +0100618
619 gprs_ns2_vc_fsm_start(nsvc);
620
621 return nsvc;
622
623err:
624 gprs_ns2_free_nsvc(nsvc);
625err_nse:
626 if (created_nse)
627 gprs_ns2_free_nse(nse);
628
629 return NULL;
630}
631
632/*! Return the nsvc by dlci.
633 * \param[in] bind
634 * \param[in] dlci Data Link connection identifier
635 * \return the nsvc or NULL if not found
636 */
637struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,
638 uint16_t dlci)
639{
640 struct gprs_ns2_vc *nsvc;
641 struct priv_vc *vcpriv;
642
643 llist_for_each_entry(nsvc, &bind->nsvc, blist) {
644 vcpriv = nsvc->priv;
645
646 if (dlci == vcpriv->dlci)
647 return nsvc;
648 }
649
650 return NULL;
651}
652
653/*! Return the dlci of the nsvc
654 * \param[in] nsvc
655 * \return the dlci or 0 on error. 0 is not a valid dlci.
656 */
657uint16_t gprs_ns2_fr_nsvc_dlci(struct gprs_ns2_vc *nsvc)
658{
659 struct priv_vc *vcpriv;
660
661 if (!nsvc->bind)
662 return 0;
663
664 if (nsvc->bind->driver != &vc_driver_fr)
665 return 0;
666
667 vcpriv = nsvc->priv;
668 return vcpriv->dlci;
669}