<p>lynxis lazus has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/21243">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">ns2: add support for frame relay<br><br>Add support for frame relay over dahdi hdlc device.<br>It's supporting lmi by q933 and supports both<br>SGSN and BSS.<br><br>Change-Id: Id3b49f93d33c271f77cd9c9db03cde6b727a4d30<br>---<br>M include/Makefile.am<br>A include/osmocom/gprs/frame_relay.h<br>M include/osmocom/gprs/gprs_ns2.h<br>M src/gb/Makefile.am<br>A src/gb/frame_relay.c<br>M src/gb/gprs_ns2.c<br>A src/gb/gprs_ns2_fr.c<br>M src/gb/gprs_ns2_internal.h<br>M src/gb/gprs_ns2_vty.c<br>M src/gb/libosmogb.map<br>10 files changed, 1,729 insertions(+), 21 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/43/21243/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/Makefile.am b/include/Makefile.am</span><br><span>index 44ff378..3173290 100644</span><br><span>--- a/include/Makefile.am</span><br><span>+++ b/include/Makefile.am</span><br><span>@@ -60,6 +60,7 @@</span><br><span>                 osmocom/ctrl/control_cmd.h \</span><br><span>                 osmocom/ctrl/control_if.h \</span><br><span>                  osmocom/ctrl/ports.h \</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmocom/gprs/frame_relay.h \</span><br><span>                        osmocom/gprs/gprs_bssgp.h \</span><br><span>                        osmocom/gprs/gprs_bssgp_bss.h \</span><br><span>                        osmocom/gprs/gprs_msgb.h \</span><br><span>diff --git a/include/osmocom/gprs/frame_relay.h b/include/osmocom/gprs/frame_relay.h</span><br><span>new file mode 100644</span><br><span>index 0000000..3004ff3</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/gprs/frame_relay.h</span><br><span>@@ -0,0 +1,112 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file frame_relay.h */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/timer.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef;</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum osmo_fr_role {</span><br><span style="color: hsl(120, 100%, 40%);">+   FR_ROLE_USER_EQUIPMENT,</span><br><span style="color: hsl(120, 100%, 40%);">+       FR_ROLE_NETWORK_EQUIPMENT,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+extern const struct value_string osmo_fr_role_names[];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline const char *osmo_fr_role_str(enum osmo_fr_role role) {</span><br><span style="color: hsl(120, 100%, 40%);">+      return get_value_string(osmo_fr_role_names, role);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_network {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct llist_head links;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int n391;              /* full status polling counter */</span><br><span style="color: hsl(120, 100%, 40%);">+     unsigned int n392;              /* error threshold */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int n393;              /* monitored events count */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_tdef *T_defs;       /* T391, T392 */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Frame Relay Link */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_link {</span><br><span style="color: hsl(120, 100%, 40%);">+  /* list in osmo_fr_network.links */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct llist_head list;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_fr_network *net;</span><br><span style="color: hsl(120, 100%, 40%);">+  enum osmo_fr_role role;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* human-readable name */</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* value of the last received send sequence number field in the</span><br><span style="color: hsl(120, 100%, 40%);">+        * link integrity verification information element */</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t last_rx_seq;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* value of the send sequence number field of the last link</span><br><span style="color: hsl(120, 100%, 40%);">+    * integrity verification information element sent */</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t last_tx_seq;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_timer_list t391;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_timer_list t392;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        unsigned int polling_count;</span><br><span style="color: hsl(120, 100%, 40%);">+   unsigned int err_count;</span><br><span style="color: hsl(120, 100%, 40%);">+       unsigned int succeed;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* the type of the last status enquiry */</span><br><span style="color: hsl(120, 100%, 40%);">+     uint8_t expected_rep;</span><br><span style="color: hsl(120, 100%, 40%);">+ bool state;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* list of data link connections at this link */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct llist_head dlc_list;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int (*unknown_dlc_rx_cb)(void *cb_data, struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+    void *unknown_dlc_rx_cb_data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       int (*tx_cb)(void *data, struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+   void *tx_cb_data;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Frame Relay Data Link Connection */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_dlc {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* entry in fr_link.dlc_list */</span><br><span style="color: hsl(120, 100%, 40%);">+       struct llist_head list;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_fr_link *link;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  uint16_t dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* is this DLC marked active for traffic? */</span><br><span style="color: hsl(120, 100%, 40%);">+  bool active;</span><br><span style="color: hsl(120, 100%, 40%);">+  /* was this DLC newly added? */</span><br><span style="color: hsl(120, 100%, 40%);">+       bool add;</span><br><span style="color: hsl(120, 100%, 40%);">+     /* is this DLC about to be destroyed */</span><br><span style="color: hsl(120, 100%, 40%);">+       bool del;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* the local state needs to be transfered to the</span><br><span style="color: hsl(120, 100%, 40%);">+       * UE. The NET must wait until the UE confirms it implicited by a seq number check */</span><br><span style="color: hsl(120, 100%, 40%);">+ bool state_send;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    int (*rx_cb)(void *cb_data, struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+        void *rx_cb_data;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* allocate a frame relay network */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_network *osmo_fr_network_alloc(void *ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* allocate a frame relay link in a given network */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_link *osmo_fr_link_alloc(struct osmo_fr_network *net, enum osmo_fr_role role, const char *name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* free a frame link in a given network */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_fr_link_free(struct osmo_fr_link *link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* allocate a data link connectoin on a given framerelay link */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_dlc *osmo_fr_dlc_alloc(struct osmo_fr_link *link, uint16_t dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_dlc *osmo_fr_dlc_by_dlci(struct osmo_fr_link *link, uint16_t dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_fr_rx(struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_fr_tx_dlc(struct msgb *msg);</span><br><span>diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h</span><br><span>index 3b47b3c..2a805c2 100644</span><br><span>--- a/include/osmocom/gprs/gprs_ns2.h</span><br><span>+++ b/include/osmocom/gprs/gprs_ns2.h</span><br><span>@@ -8,9 +8,11 @@</span><br><span> </span><br><span> #include <osmocom/core/prim.h></span><br><span> #include <osmocom/gprs/protocol/gsm_08_16.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gprs/frame_relay.h></span><br><span> </span><br><span> struct osmo_sockaddr;</span><br><span> struct osmo_sockaddr_str;</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_network;</span><br><span> </span><br><span> struct gprs_ns2_inst;</span><br><span> struct gprs_ns2_nse;</span><br><span>@@ -146,6 +148,23 @@</span><br><span>                                                      const struct osmo_sockaddr *sockaddr);</span><br><span> void gprs_ns2_bind_set_mode(struct gprs_ns2_vc_bind *bind, enum gprs_ns2_vc_mode mode);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* FR VL driver */</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(</span><br><span style="color: hsl(120, 100%, 40%);">+          struct gprs_ns2_inst *nsi,</span><br><span style="color: hsl(120, 100%, 40%);">+            const char *netif);</span><br><span style="color: hsl(120, 100%, 40%);">+const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind);</span><br><span style="color: hsl(120, 100%, 40%);">+int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,</span><br><span style="color: hsl(120, 100%, 40%);">+                   const char *netif,</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct osmo_fr_network *fr_network,</span><br><span style="color: hsl(120, 100%, 40%);">+                   enum osmo_fr_role fr_role,</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct gprs_ns2_vc_bind **result);</span><br><span style="color: hsl(120, 100%, 40%);">+int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind);</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind, uint16_t dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 uint16_t nsei,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        uint16_t nsvci,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       uint16_t dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* create a VC connection */</span><br><span> struct gprs_ns2_vc *gprs_ns2_ip_connect(struct gprs_ns2_vc_bind *bind,</span><br><span>                                  const struct osmo_sockaddr *remote,</span><br><span>diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am</span><br><span>index 65c3552..41b6c6d 100644</span><br><span>--- a/src/gb/Makefile.am</span><br><span>+++ b/src/gb/Makefile.am</span><br><span>@@ -21,9 +21,9 @@</span><br><span> libosmogb_la_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c gprs_ns_sns.c \</span><br><span>                 gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c \</span><br><span>            gprs_bssgp_bss.c \</span><br><span style="color: hsl(0, 100%, 40%);">-              gprs_ns2.c gprs_ns2_udp.c gprs_ns2_frgre.c gprs_ns2_vc_fsm.c gprs_ns2_sns.c \</span><br><span style="color: hsl(120, 100%, 40%);">+                 gprs_ns2.c gprs_ns2_udp.c gprs_ns2_frgre.c gprs_ns2_fr.c gprs_ns2_vc_fsm.c gprs_ns2_sns.c \</span><br><span>                  gprs_ns2_message.c gprs_ns2_vty.c \</span><br><span style="color: hsl(0, 100%, 40%);">-             common_vty.c</span><br><span style="color: hsl(120, 100%, 40%);">+                  common_vty.c frame_relay.c</span><br><span> endif</span><br><span> </span><br><span> EXTRA_DIST = libosmogb.map</span><br><span>diff --git a/src/gb/frame_relay.c b/src/gb/frame_relay.c</span><br><span>new file mode 100644</span><br><span>index 0000000..c96a1ca</span><br><span>--- /dev/null</span><br><span>+++ b/src/gb/frame_relay.c</span><br><span>@@ -0,0 +1,900 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdbool.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gprs/frame_relay.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/endian.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tdef.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/timer.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/tlv.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOGPFRL(frl, lvl, fmt, args ...) \</span><br><span style="color: hsl(120, 100%, 40%);">+      LOGP(DFR, lvl, "%s: " fmt, (frl)->name, ## args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define DFR DLNS</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Table 4-2/Q.931 */</span><br><span style="color: hsl(120, 100%, 40%);">+enum q931_msgtype {</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Call establishment message */</span><br><span style="color: hsl(120, 100%, 40%);">+      Q931_MSGT_ALERTING              = 0x01,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_CALL_PROCEEDING       = 0x02,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_CONNECT               = 0x07,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_CONNECT_ACK           = 0x0f,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_PROGRESS              = 0x03,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_SETUP                 = 0x05,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_SETUP_ACK             = 0x0d,</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Call information phase message */</span><br><span style="color: hsl(120, 100%, 40%);">+  Q931_MSGT_RESUME                = 0x26,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_RESUME_ACK            = 0x2e,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_RESUME_REJ            = 0x22,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_SUSPEND               = 0x25,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_SUSPEND_ACK           = 0x2d,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_USER_INFO             = 0x20,</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Call clearing message */</span><br><span style="color: hsl(120, 100%, 40%);">+   Q931_MSGT_DISCONNECT            = 0x45,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_RELEASE               = 0x4d,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_RELEASE_COMPLETE      = 0x5a,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_RESTART               = 0x46,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_RESTART_ACK           = 0x4e,</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Miscellaneous messages */</span><br><span style="color: hsl(120, 100%, 40%);">+  Q931_MSGT_SEGMENT               = 0x60,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_CONGESTION_CONTROL    = 0x79,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_IFORMATION            = 0x7b,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_NOTIFY                = 0x6e,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_STATUS                = 0x7d,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q931_MSGT_STATUS_ENQUIRY        = 0x75,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Figure A.1/Q.933 Report type information element */</span><br><span style="color: hsl(120, 100%, 40%);">+enum q933_type_of_report {</span><br><span style="color: hsl(120, 100%, 40%);">+   Q933_REPT_FULL_STATUS           = 0x00,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q933_REPT_LINK_INTEGRITY_VERIF  = 0x01,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q933_REPT_SINGLE_PVC_ASYNC_STS  = 0x02,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Q.933 Section A.3 */</span><br><span style="color: hsl(120, 100%, 40%);">+enum q933_iei {</span><br><span style="color: hsl(120, 100%, 40%);">+       Q933_IEI_REPORT_TYPE            = 0x51,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q933_IEI_LINK_INT_VERIF         = 0x53,</span><br><span style="color: hsl(120, 100%, 40%);">+       Q933_IEI_PVC_STATUS             = 0x57,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LAPF_UI                  0x03    /* UI control word */</span><br><span style="color: hsl(120, 100%, 40%);">+#define Q931_PDISC_CC            0x08    /* protocol discriminator */</span><br><span style="color: hsl(120, 100%, 40%);">+#define LMI_Q933A_CALLREF 0x00    /* NULL call-ref */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* LMI DLCI values */</span><br><span style="color: hsl(120, 100%, 40%);">+#define LMI_Q933A_DLCI            0       /* Q.933A DLCI */</span><br><span style="color: hsl(120, 100%, 40%);">+#define LMI_CISCO_DLCI               1023    /* Cisco DLCI */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* maximum of supported */</span><br><span style="color: hsl(120, 100%, 40%);">+#define MAX_SUPPORTED_PVC       10</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* TODO: add counters since good connection */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Message header of the L3 payload of a Q.933 Annex A message */</span><br><span style="color: hsl(120, 100%, 40%);">+struct q933_a_hdr {</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t prot_disc;</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t call_ref;</span><br><span style="color: hsl(120, 100%, 40%);">+     uint8_t msg_type;</span><br><span style="color: hsl(120, 100%, 40%);">+} __attribute__((packed));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Value part of the Q.933 Annex A.3.3 IE */</span><br><span style="color: hsl(120, 100%, 40%);">+struct q933_a_pvc_sts {</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t dlci_msb:6,</span><br><span style="color: hsl(120, 100%, 40%);">+           spare:1,</span><br><span style="color: hsl(120, 100%, 40%);">+              ext0:1;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t space1:3,</span><br><span style="color: hsl(120, 100%, 40%);">+             dlci_lsb:4,</span><br><span style="color: hsl(120, 100%, 40%);">+           ext1:1;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t reserved:1,</span><br><span style="color: hsl(120, 100%, 40%);">+           active:1,</span><br><span style="color: hsl(120, 100%, 40%);">+             delete:1,</span><br><span style="color: hsl(120, 100%, 40%);">+             new:1,</span><br><span style="color: hsl(120, 100%, 40%);">+                spare2:3,</span><br><span style="color: hsl(120, 100%, 40%);">+             ext2:1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+} __attribute__((packed));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* RX Message: 14 [ 00 01 03 08 00 75  95 01 01 00 03 02 01 00 ] */</span><br><span style="color: hsl(120, 100%, 40%);">+/* RX Message: 13 [ 00 01 03 08 00 75  51 01 00  53 02 01 00 ] */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct value_string osmo_fr_role_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ { FR_ROLE_USER_EQUIPMENT,       "USER" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { FR_ROLE_NETWORK_EQUIPMENT,    "NETWORK" },</span><br><span style="color: hsl(120, 100%, 40%);">+        { 0, NULL }</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Table A.4/Q.933 */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tdef fr_tdefs[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+             .T=391,</span><br><span style="color: hsl(120, 100%, 40%);">+//             .default_val = 10,</span><br><span style="color: hsl(120, 100%, 40%);">+    .default_val = 5,</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           .min_val = 5,</span><br><span style="color: hsl(120, 100%, 40%);">+         .max_val = 30,</span><br><span style="color: hsl(120, 100%, 40%);">+                .desc = "Link integrity verification polling timer",</span><br><span style="color: hsl(120, 100%, 40%);">+                        .unit =  OSMO_TDEF_S,</span><br><span style="color: hsl(120, 100%, 40%);">+ }, {</span><br><span style="color: hsl(120, 100%, 40%);">+          .T=392,</span><br><span style="color: hsl(120, 100%, 40%);">+               .default_val = 15,</span><br><span style="color: hsl(120, 100%, 40%);">+            .min_val = 5,</span><br><span style="color: hsl(120, 100%, 40%);">+         .max_val = 30,</span><br><span style="color: hsl(120, 100%, 40%);">+                .desc = "Polling verification timer",</span><br><span style="color: hsl(120, 100%, 40%);">+                               .unit =  OSMO_TDEF_S,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+    {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct tlv_definition q933_att_tlvdef = {</span><br><span style="color: hsl(120, 100%, 40%);">+      .def = {</span><br><span style="color: hsl(120, 100%, 40%);">+              [Q933_IEI_REPORT_TYPE] = { TLV_TYPE_TLV },</span><br><span style="color: hsl(120, 100%, 40%);">+            [Q933_IEI_LINK_INT_VERIF] = { TLV_TYPE_TLV },</span><br><span style="color: hsl(120, 100%, 40%);">+         [Q933_IEI_PVC_STATUS] = { TLV_TYPE_TLV },</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void check_link_state(struct osmo_fr_link *link, bool valid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t q922_to_dlci(const uint8_t *hdr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline void dlci_to_q922(uint8_t *hdr, uint16_t dlci)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   hdr[0] = (dlci >> 2) & 0xFC;</span><br><span style="color: hsl(120, 100%, 40%);">+        hdr[1] = ((dlci << 4) & 0xF0) | 0x01;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* allocate a message buffer and put Q.933 Annex A headers (L2 + L3) */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct msgb *q933_msgb_alloc(uint16_t dlci, uint8_t prot_disc, uint8_t msg_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct msgb *msg = msgb_alloc_headroom(1600+64, 64, "FR Q.933 Tx");</span><br><span style="color: hsl(120, 100%, 40%);">+ struct q933_a_hdr *qh;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!msg)</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        msg->l1h = msgb_put(msg, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+       dlci_to_q922(msg->l1h, dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* LAPF UI control */</span><br><span style="color: hsl(120, 100%, 40%);">+ msg->l2h = msgb_put(msg, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+       *msg->l2h = LAPF_UI;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     msg->l3h = msgb_put(msg, sizeof(*qh));</span><br><span style="color: hsl(120, 100%, 40%);">+     qh = (struct q933_a_hdr *) msg->l3h;</span><br><span style="color: hsl(120, 100%, 40%);">+       qh->prot_disc = prot_disc;</span><br><span style="color: hsl(120, 100%, 40%);">+ qh->call_ref = LMI_Q933A_CALLREF;</span><br><span style="color: hsl(120, 100%, 40%);">+  qh->msg_type = msg_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* obtain the [next] transmit sequence number */</span><br><span style="color: hsl(120, 100%, 40%);">+static uint8_t link_get_tx_seq(struct osmo_fr_link *link)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      /* The {user equipment, network} increments the send sequence</span><br><span style="color: hsl(120, 100%, 40%);">+  * counter using modulo 256. The value zero is skipped. */</span><br><span style="color: hsl(120, 100%, 40%);">+    link->last_tx_seq++;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (link->last_tx_seq == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                link->last_tx_seq++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return link->last_tx_seq;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Append PVC Status IE according to Q.933 A.3.2 */</span><br><span style="color: hsl(120, 100%, 40%);">+static void msgb_put_link_int_verif(struct msgb *msg, struct osmo_fr_link *link)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t link_int_tx[2];</span><br><span style="color: hsl(120, 100%, 40%);">+       link_int_tx[0] = link_get_tx_seq(link);</span><br><span style="color: hsl(120, 100%, 40%);">+       link_int_tx[1] = link->last_rx_seq;</span><br><span style="color: hsl(120, 100%, 40%);">+        msgb_tlv_put(msg, Q933_IEI_LINK_INT_VERIF, 2, link_int_tx);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void dlc_destroy(struct osmo_fr_dlc *dlc)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_del(&dlc->list);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(dlc);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Append PVC Status IE according to Q.933 A.3.3 */</span><br><span style="color: hsl(120, 100%, 40%);">+static void msgb_put_pvc_status(struct msgb *msg, struct osmo_fr_dlc *dlc)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t ie[3];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ie[0] = (dlc->dlci >> 4) & 0x3f;</span><br><span style="color: hsl(120, 100%, 40%);">+ ie[1] = 0x80 | ((dlc->dlci & 0xf) << 3);</span><br><span style="color: hsl(120, 100%, 40%);">+ ie[2] = 0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* FIXME: validate: this status should be added as long it's not yet acked by the remote */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (dlc->active)</span><br><span style="color: hsl(120, 100%, 40%);">+           ie[2] |= 0x02;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (dlc->add) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ie[2] |= 0x08;</span><br><span style="color: hsl(120, 100%, 40%);">+                /* we've reported it as new once, reset the status */</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (dlc->del) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ie[2] |= 0x04;</span><br><span style="color: hsl(120, 100%, 40%);">+                /* we've reported it as deleted once, destroy it */</span><br><span style="color: hsl(120, 100%, 40%);">+               dlc_destroy(dlc);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msgb_tlv_put(msg, Q933_IEI_PVC_STATUS, 3, ie);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Send a Q.933 STATUS ENQUIRY given type over given link */</span><br><span style="color: hsl(120, 100%, 40%);">+static int tx_lmi_q933_status_enq(struct osmo_fr_link *link, uint8_t rep_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct msgb *resp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  resp = q933_msgb_alloc(0, Q931_PDISC_CC, Q931_MSGT_STATUS_ENQUIRY);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!resp)</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    resp->dst = link;</span><br><span style="color: hsl(120, 100%, 40%);">+  link->expected_rep = rep_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Table A.2/Q.933 */</span><br><span style="color: hsl(120, 100%, 40%);">+ msgb_tlv_put(resp, Q933_IEI_REPORT_TYPE, 1, &rep_type);</span><br><span style="color: hsl(120, 100%, 40%);">+   msgb_put_link_int_verif(resp, link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return link->tx_cb(link->tx_cb_data, resp);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Send a Q.933 STATUS of given type over given link */</span><br><span style="color: hsl(120, 100%, 40%);">+static int tx_lmi_q933_status(struct osmo_fr_link *link, uint8_t rep_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_fr_dlc *dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct msgb *resp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  resp = q933_msgb_alloc(0, Q931_PDISC_CC, Q931_MSGT_STATUS);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!resp)</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  resp->dst = link;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Table A.1/Q.933 */</span><br><span style="color: hsl(120, 100%, 40%);">+ msgb_tlv_put(resp, Q933_IEI_REPORT_TYPE, 1, &rep_type);</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (rep_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+   case Q933_REPT_FULL_STATUS:</span><br><span style="color: hsl(120, 100%, 40%);">+           msgb_put_link_int_verif(resp, link);</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_for_each_entry(dlc, &link->dlc_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (dlc->add || dlc->del)</span><br><span style="color: hsl(120, 100%, 40%);">+                               dlc->state_send = true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                  msgb_put_pvc_status(resp, dlc);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case Q933_REPT_LINK_INTEGRITY_VERIF:</span><br><span style="color: hsl(120, 100%, 40%);">+          msgb_put_link_int_verif(resp, link);</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_for_each_entry(dlc, &link->dlc_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (dlc->add || dlc->del) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             msgb_put_pvc_status(resp, dlc);</span><br><span style="color: hsl(120, 100%, 40%);">+                               dlc->state_send = true;</span><br><span style="color: hsl(120, 100%, 40%);">+                    }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case Q933_REPT_SINGLE_PVC_ASYNC_STS:</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_for_each_entry(dlc, &link->dlc_list, list)</span><br><span style="color: hsl(120, 100%, 40%);">+                       msgb_put_pvc_status(resp, dlc);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return link->tx_cb(link->tx_cb_data, resp);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Q.933 */</span><br><span style="color: hsl(120, 100%, 40%);">+static int rx_lmi_q933_status_enq(struct msgb *msg, struct tlv_parsed *tp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_fr_link *link = msg->dst;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_fr_dlc *dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+      const uint8_t *link_int_rx;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t rep_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (link->role == FR_ROLE_USER_EQUIPMENT) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGPFRL(link, LOGL_ERROR, "STATUS-ENQ aren't support for role user\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* check for mandatory IEs */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!TLVP_PRES_LEN(tp, Q933_IEI_REPORT_TYPE, 1) ||</span><br><span style="color: hsl(120, 100%, 40%);">+        !TLVP_PRES_LEN(tp, Q933_IEI_LINK_INT_VERIF, 2))</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  rep_type = *TLVP_VAL(tp, Q933_IEI_REPORT_TYPE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     link_int_rx = TLVP_VAL(tp, Q933_IEI_LINK_INT_VERIF);</span><br><span style="color: hsl(120, 100%, 40%);">+  link->last_rx_seq = link_int_rx[0];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* the network checks the receive sequence number received from</span><br><span style="color: hsl(120, 100%, 40%);">+        * the user equipment against its send sequence counter */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (link_int_rx[1] != link->last_tx_seq) {</span><br><span style="color: hsl(120, 100%, 40%);">+         check_link_state(link, false);</span><br><span style="color: hsl(120, 100%, 40%);">+                link->err_count++;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              check_link_state(link, true);</span><br><span style="color: hsl(120, 100%, 40%);">+         /* confirm DLC state changes */</span><br><span style="color: hsl(120, 100%, 40%);">+               llist_for_each_entry(dlc, &link->dlc_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (!dlc->state_send)</span><br><span style="color: hsl(120, 100%, 40%);">+                              continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (dlc->add) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            dlc->active = link->state;</span><br><span style="color: hsl(120, 100%, 40%);">+                              dlc->add = false;</span><br><span style="color: hsl(120, 100%, 40%);">+                  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (dlc->del) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            dlc->del = false;</span><br><span style="color: hsl(120, 100%, 40%);">+                          /* TODO: implement FRNET free */</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   dlc->state_send = false;</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The network responds to each STATUS ENQUIRY message with a</span><br><span style="color: hsl(120, 100%, 40%);">+  * STATUS message and resets the T392 timer */</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return tx_lmi_q933_status(link, rep_type);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* check if the link become active or not.</span><br><span style="color: hsl(120, 100%, 40%);">+ * param[in] valid contains the status of the last packet */</span><br><span style="color: hsl(120, 100%, 40%);">+static void check_link_state(struct osmo_fr_link *link, bool valid)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     unsigned int last, i;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int carry = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_fr_dlc *dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    link->succeed = link->succeed << 1;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (valid)</span><br><span style="color: hsl(120, 100%, 40%);">+            link->succeed |= 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* count the bits */</span><br><span style="color: hsl(120, 100%, 40%);">+  last = link->succeed & ((1 << link->net->n393) - 1);</span><br><span style="color: hsl(120, 100%, 40%);">+       for (i = 0; i < link->net->n393; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+                if (last & 1 << i)</span><br><span style="color: hsl(120, 100%, 40%);">+                  carry++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (link->net->n393 - carry >= link->net->n392) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* failing link */</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!link->state)</span><br><span style="color: hsl(120, 100%, 40%);">+                  return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGPFRL(link, LOGL_NOTICE, "Link failed\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                link->state = false;</span><br><span style="color: hsl(120, 100%, 40%);">+               if (link->role == FR_ROLE_USER_EQUIPMENT)</span><br><span style="color: hsl(120, 100%, 40%);">+                  return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_for_each_entry(dlc, &link->dlc_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     dlc->active = false;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* good link */</span><br><span style="color: hsl(120, 100%, 40%);">+               if (link->state)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGPFRL(link, LOGL_NOTICE, "Link recovered\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             link->state = true;</span><br><span style="color: hsl(120, 100%, 40%);">+                if (link->role == FR_ROLE_USER_EQUIPMENT)</span><br><span style="color: hsl(120, 100%, 40%);">+                  return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_for_each_entry(dlc, &link->dlc_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (!dlc->add && !dlc->del)</span><br><span style="color: hsl(120, 100%, 40%);">+                             dlc->active = true;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int validate_pvc_status(struct tlv_parsed *tp, size_t tp_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+     uint16_t len = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < tp_len; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* PVC status can be 2 or 3 bytes. If the PVC is bigger</span><br><span style="color: hsl(120, 100%, 40%);">+                * ignore this to be compatible to future extensions. */</span><br><span style="color: hsl(120, 100%, 40%);">+              len = TLVP_LEN(&tp[i], Q933_IEI_PVC_STATUS);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (len <= 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             /* FIXME: validate correct flags: are some flags invalid at the same time? */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int parse_full_pvc_status(struct osmo_fr_link *link, struct tlv_parsed *tp, size_t tp_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+     int err = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_fr_dlc *dlc, *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct q933_a_pvc_sts *pvc;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint16_t dlci = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    uint16_t *dlcis = talloc_zero_array(link, uint16_t, tp_len);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!dlcis)</span><br><span style="color: hsl(120, 100%, 40%);">+           return -ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* first run validate all PVCs */</span><br><span style="color: hsl(120, 100%, 40%);">+     err = validate_pvc_status(tp, tp_len);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (err < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+               goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < tp_len; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* parse only 3 byte PVCs */</span><br><span style="color: hsl(120, 100%, 40%);">+          pvc = (struct q933_a_pvc_sts *) TLVP_VAL_MINLEN(</span><br><span style="color: hsl(120, 100%, 40%);">+                                      &tp[i],</span><br><span style="color: hsl(120, 100%, 40%);">+                                   Q933_IEI_PVC_STATUS,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  sizeof(struct q933_a_pvc_sts));</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!pvc)</span><br><span style="color: hsl(120, 100%, 40%);">+                     continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           dlci = ((pvc->dlci_msb & 0x3f) << 4) | (pvc->dlci_lsb & 0xf);</span><br><span style="color: hsl(120, 100%, 40%);">+             dlcis[i] = dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+              dlc = osmo_fr_dlc_by_dlci(link, dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!dlc) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   dlc = osmo_fr_dlc_alloc(link, dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (!dlc) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           LOGPFRL(link, LOGL_ERROR, "Could not create DLC %d\n", dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+                       }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Figure A.3/Q.933: The delete bit is only applicable for timely notification</span><br><span style="color: hsl(120, 100%, 40%);">+                 *                   using the optional single PVC asynchronous status report.</span><br><span style="color: hsl(120, 100%, 40%);">+                 * Ignoring the delete. */</span><br><span style="color: hsl(120, 100%, 40%);">+            dlc->add = pvc->new;</span><br><span style="color: hsl(120, 100%, 40%);">+            dlc->active = pvc->active;</span><br><span style="color: hsl(120, 100%, 40%);">+              dlc->del = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* check if all dlc are present in PVC Status */</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_for_each_entry_safe(dlc, tmp, &link->dlc_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+           bool found = false;</span><br><span style="color: hsl(120, 100%, 40%);">+           for (i = 0; i < tp_len; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (dlcis[i] == dlc->dlci) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               found = true;</span><br><span style="color: hsl(120, 100%, 40%);">+                         break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!found) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 dlc->active = false;</span><br><span style="color: hsl(120, 100%, 40%);">+                       dlc->del = true;</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+out:</span><br><span style="color: hsl(120, 100%, 40%);">+       talloc_free(dlcis);</span><br><span style="color: hsl(120, 100%, 40%);">+   return err;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int parse_link_pvc_status(struct osmo_fr_link *link, struct tlv_parsed *tp, size_t tp_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int err;</span><br><span style="color: hsl(120, 100%, 40%);">+      size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct q933_a_pvc_sts *pvc;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_fr_dlc *dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+      uint16_t dlci = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  err = validate_pvc_status(tp, tp_len);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (err < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+               return err;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < tp_len; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* parse only 3 byte PVCs */</span><br><span style="color: hsl(120, 100%, 40%);">+          pvc = (struct q933_a_pvc_sts *) TLVP_VAL_MINLEN(</span><br><span style="color: hsl(120, 100%, 40%);">+                                      &tp[i],</span><br><span style="color: hsl(120, 100%, 40%);">+                                   Q933_IEI_PVC_STATUS,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  sizeof(struct q933_a_pvc_sts));</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!pvc)</span><br><span style="color: hsl(120, 100%, 40%);">+                     continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           dlci = ((pvc->dlci_msb & 0x3f) << 4) | (pvc->dlci_lsb & 0xf);</span><br><span style="color: hsl(120, 100%, 40%);">+             dlc = osmo_fr_dlc_by_dlci(link, dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!dlc) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   /* don't create dlc's for the ones which are about to be deleted. */</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (dlc->del)</span><br><span style="color: hsl(120, 100%, 40%);">+                              continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   dlc = osmo_fr_dlc_alloc(link, dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (!dlc) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           LOGPFRL(link, LOGL_ERROR, "Rx STATUS: Could not create DLC %d\n", dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+                    }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (pvc->delete) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 dlc->del = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+              } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      dlc->add = pvc->new;</span><br><span style="color: hsl(120, 100%, 40%);">+                    dlc->active = pvc->active;</span><br><span style="color: hsl(120, 100%, 40%);">+                      dlc->del = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static size_t count_pvc_status(struct tlv_parsed *tp, size_t tp_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       size_t i, count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  for (i = 0; i < tp_len; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             count++;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return count;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int rx_lmi_q933_status(struct msgb *msg, struct tlv_parsed *tp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_fr_link *link = msg->dst;</span><br><span style="color: hsl(120, 100%, 40%);">+      const uint8_t *link_int_rx;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t rep_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (link->role == FR_ROLE_NETWORK_EQUIPMENT) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGPFRL(link, LOGL_ERROR, "Rx STATUS: STATUS aren't support for role network\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* check for mandatory IEs */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!TLVP_PRES_LEN(tp, Q933_IEI_REPORT_TYPE, 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGPFRL(link, LOGL_NOTICE, "Rx STATUSL: Missing TLV Q933 Report Type\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rep_type = *TLVP_VAL(tp, Q933_IEI_REPORT_TYPE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (rep_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+   case Q933_REPT_FULL_STATUS:</span><br><span style="color: hsl(120, 100%, 40%);">+   case Q933_REPT_LINK_INTEGRITY_VERIF:</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rep_type != link->expected_rep) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOGPFRL(link, LOGL_NOTICE, "Rx STATUS: Unexpected Q933 report type (got 0x%x != exp 0x%x)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                            rep_type, link->expected_rep);</span><br><span style="color: hsl(120, 100%, 40%);">+                        return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!TLVP_PRES_LEN(tp, Q933_IEI_LINK_INT_VERIF, 2)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 LOGPFRL(link, LOGL_NOTICE, "Rx STATUS: Missing TLV Q933 Link Integrety Verification\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                    return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+             link_int_rx = TLVP_VAL(tp, Q933_IEI_LINK_INT_VERIF);</span><br><span style="color: hsl(120, 100%, 40%);">+          link->last_rx_seq = link_int_rx[0];</span><br><span style="color: hsl(120, 100%, 40%);">+                /* The received receive sequence number is not valid if</span><br><span style="color: hsl(120, 100%, 40%);">+                * it is not equal to the last transmitted send sequence</span><br><span style="color: hsl(120, 100%, 40%);">+               * number. Ignore messages containing this error. As a</span><br><span style="color: hsl(120, 100%, 40%);">+                 * result, timer T391 expires and the user then</span><br><span style="color: hsl(120, 100%, 40%);">+                * increments the error count. */</span><br><span style="color: hsl(120, 100%, 40%);">+             if (link_int_rx[1] != link->last_tx_seq)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case Q933_REPT_SINGLE_PVC_ASYNC_STS:</span><br><span style="color: hsl(120, 100%, 40%);">+  default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   check_link_state(link, true);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (count_pvc_status(tp, MAX_SUPPORTED_PVC + 1) > MAX_SUPPORTED_PVC) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGPFRL(link, LOGL_ERROR, "Rx STATUS: Too many PVC! Only %d are supported!\n", MAX_SUPPORTED_PVC);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (rep_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+   case Q933_REPT_FULL_STATUS:</span><br><span style="color: hsl(120, 100%, 40%);">+           parse_full_pvc_status(link, tp, MAX_SUPPORTED_PVC);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case Q933_REPT_LINK_INTEGRITY_VERIF:</span><br><span style="color: hsl(120, 100%, 40%);">+          parse_link_pvc_status(link, tp, MAX_SUPPORTED_PVC);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* The network responds to each STATUS ENQUIRY message with a</span><br><span style="color: hsl(120, 100%, 40%);">+  * STATUS message and resets the T392 timer */</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int rx_lmi_q922(struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_fr_link *link = msg->dst;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct q933_a_hdr *qh;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* the + 1 is used to detect more than MAX_SUPPORTED_PVC */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct tlv_parsed tp[MAX_SUPPORTED_PVC + 1];</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t *lapf;</span><br><span style="color: hsl(120, 100%, 40%);">+        int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (msgb_l2len(msg) < 1)</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    lapf = msgb_l2(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* we only support LAPF UI frames */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (lapf[0] != LAPF_UI)</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  msg->l3h = msg->l2h + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+        if (msgb_l3len(msg) < 3)</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  qh = (struct q933_a_hdr *) msgb_l3(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (qh->prot_disc != Q931_PDISC_CC) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGPFRL(link, LOGL_NOTICE, "Rx unsupported LMI protocol discriminator %u\n", qh->prot_disc);</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   tlv_parse2(tp, MAX_SUPPORTED_PVC + 1, &q933_att_tlvdef, msgb_l3(msg) + sizeof(*qh), msgb_l3len(msg) - sizeof(*qh), 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       switch (qh->msg_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+    case Q931_MSGT_STATUS_ENQUIRY:</span><br><span style="color: hsl(120, 100%, 40%);">+                rc = rx_lmi_q933_status_enq(msg, tp);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case Q931_MSGT_STATUS:</span><br><span style="color: hsl(120, 100%, 40%);">+                rc = rx_lmi_q933_status(msg, tp);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGPFRL(link, LOGL_NOTICE, "Rx unsupported LMI message type %u\n", qh->msg_type);</span><br><span style="color: hsl(120, 100%, 40%);">+                rc = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_fr_rx(struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t *frh;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_fr_dlc *dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_fr_link *link = msg->dst;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (msgb_length(msg) < 2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGPFRL(link, LOGL_ERROR, "Rx short FR header: %u bytes\n", msgb_length(msg));</span><br><span style="color: hsl(120, 100%, 40%);">+              rc = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+              goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   frh = msg->l1h = msgb_data(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (frh[0] & 0x01) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGPFRL(link, LOGL_NOTICE, "Rx Unsupported single-byte FR address\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              rc = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+              goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if ((frh[1] & 0x0f) != 0x01) {</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGPFRL(link, LOGL_NOTICE, "Rx Unknown second FR octet 0x%02x\n", frh[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+          rc = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+              goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     dlci = q922_to_dlci(frh);</span><br><span style="color: hsl(120, 100%, 40%);">+     msg->l2h = frh + 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (dlci) {</span><br><span style="color: hsl(120, 100%, 40%);">+       case LMI_Q933A_DLCI:</span><br><span style="color: hsl(120, 100%, 40%);">+          return rx_lmi_q922(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+      case LMI_CISCO_DLCI:</span><br><span style="color: hsl(120, 100%, 40%);">+          LOGPFRL(link, LOGL_ERROR, "Rx Unsupported FR DLCI %u\n", dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+             goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!link->state) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGPFRL(link, LOGL_NOTICE, "Link is not reliable. Discarding Rx PDU on DLCI %d\n", dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+           goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry(dlc, &link->dlc_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (dlc->dlci == dlci) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   /* dispatch to handler of respective DLC */</span><br><span style="color: hsl(120, 100%, 40%);">+                   msg->dst = dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+                    return dlc->rx_cb(dlc->rx_cb_data, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (link->unknown_dlc_rx_cb)</span><br><span style="color: hsl(120, 100%, 40%);">+               return link->unknown_dlc_rx_cb(link->unknown_dlc_rx_cb_data, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+      else</span><br><span style="color: hsl(120, 100%, 40%);">+          LOGPFRL(link, LOGL_NOTICE, "DLCI %u doesn't exist, discarding\n", dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+out:</span><br><span style="color: hsl(120, 100%, 40%);">+        msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_fr_tx_dlc(struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      uint8_t *frh;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_fr_dlc *dlc = msg->dst;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_fr_link *link = dlc->link;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(dlc);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!link->state) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGPFRL(link, LOGL_NOTICE, "Link is not reliable (yet), discarding Tx\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!dlc->active) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGPFRL(link, LOGL_NOTICE, "DLCI %u is not active (yet), discarding Tx\n", dlc->dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+           msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGPFRL(link, LOGL_DEBUG, "DLCI %u is active, sending message\n", dlc->dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (msgb_headroom(msg) < 2) {</span><br><span style="color: hsl(120, 100%, 40%);">+              msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -ENOSPC;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   frh = msgb_push(msg, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+      dlci_to_q922(frh, dlc->dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    msg->dst = link;</span><br><span style="color: hsl(120, 100%, 40%);">+   return link->tx_cb(link->tx_cb_data, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Every T391 seconds, the user equipment sends a STATUS ENQUIRY</span><br><span style="color: hsl(120, 100%, 40%);">+ * message to the network and resets its polling timer (T391). */</span><br><span style="color: hsl(120, 100%, 40%);">+static void fr_t391_cb(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fr_link *link = data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (link->polling_count % link->net->n391 == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+              tx_lmi_q933_status_enq(link, Q933_REPT_FULL_STATUS);</span><br><span style="color: hsl(120, 100%, 40%);">+  else</span><br><span style="color: hsl(120, 100%, 40%);">+          tx_lmi_q933_status_enq(link, Q933_REPT_LINK_INTEGRITY_VERIF);</span><br><span style="color: hsl(120, 100%, 40%);">+ link->polling_count++;</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_schedule(&link->t391, osmo_tdef_get(link->net->T_defs, 391, OSMO_TDEF_S, 10), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void fr_t392_cb(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_fr_link *link = data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* A.5 The network increments the error count .. Non-receipt of</span><br><span style="color: hsl(120, 100%, 40%);">+        * a STATUS ENQUIRY within T392, which results in restarting</span><br><span style="color: hsl(120, 100%, 40%);">+   * T392 */</span><br><span style="color: hsl(120, 100%, 40%);">+    link->err_count++;</span><br><span style="color: hsl(120, 100%, 40%);">+ check_link_state(link, false);</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* allocate a frame relay network */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_network *osmo_fr_network_alloc(void *ctx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fr_network *net = talloc_zero(ctx, struct osmo_fr_network);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     INIT_LLIST_HEAD(&net->links);</span><br><span style="color: hsl(120, 100%, 40%);">+  net->T_defs = fr_tdefs;</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_tdefs_reset(net->T_defs);</span><br><span style="color: hsl(120, 100%, 40%);">+     net->n391 = 6;</span><br><span style="color: hsl(120, 100%, 40%);">+     net->n392 = 3;</span><br><span style="color: hsl(120, 100%, 40%);">+     net->n393 = 4;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return net;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_fr_network_free(struct osmo_fr_network *net)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_fr_link *link, *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!net)</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry_safe(link, tmp, &net->links, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_fr_link_free(link);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* allocate a frame relay link in a given network */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_link *osmo_fr_link_alloc(struct osmo_fr_network *net, enum osmo_fr_role role, const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_fr_link *link = talloc_zero(net, struct osmo_fr_link);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!link)</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGPFRL(link, LOGL_INFO, "Creating frame relay link with role %s\n", osmo_fr_role_str(role));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     link->role = role;</span><br><span style="color: hsl(120, 100%, 40%);">+ link->net = net;</span><br><span style="color: hsl(120, 100%, 40%);">+   link->name = talloc_strdup(link, name);</span><br><span style="color: hsl(120, 100%, 40%);">+    INIT_LLIST_HEAD(&link->dlc_list);</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_add_tail(&link->list, &net->links);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_setup(&link->t391, fr_t391_cb, link);</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_timer_setup(&link->t392, fr_t392_cb, link);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (role) {</span><br><span style="color: hsl(120, 100%, 40%);">+       case FR_ROLE_USER_EQUIPMENT:</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_timer_schedule(&link->t391, osmo_tdef_get(link->net->T_defs, 391, OSMO_TDEF_S, 15), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case FR_ROLE_NETWORK_EQUIPMENT:</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return link;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_fr_link_free(struct osmo_fr_link *link)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!link)</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_del(&link->t391);</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_timer_del(&link->t392);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_del(&link->list);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(link);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* allocate a data link connectoin on a given framerelay link */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_dlc *osmo_fr_dlc_alloc(struct osmo_fr_link *link, uint16_t dlci)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_fr_dlc *dlc = talloc_zero(link, struct osmo_fr_dlc);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!dlc)</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        dlc->link = link;</span><br><span style="color: hsl(120, 100%, 40%);">+  dlc->dlci = dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+  dlc->active = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_add_tail(&dlc->list, &link->dlc_list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  dlc->add = true;</span><br><span style="color: hsl(120, 100%, 40%);">+   tx_lmi_q933_status(link, Q933_IEI_PVC_STATUS);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* TODO: osmo_fr_dlc_alloc with deregistering it from the link in fr-net */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_fr_dlc *osmo_fr_dlc_by_dlci(struct osmo_fr_link *link, uint16_t dlci)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_fr_dlc *dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    llist_for_each_entry(dlc, &link->dlc_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (dlc->dlci == dlci)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c</span><br><span>index cf04924..4b32ddf 100644</span><br><span>--- a/src/gb/gprs_ns2.c</span><br><span>+++ b/src/gb/gprs_ns2.c</span><br><span>@@ -258,6 +258,9 @@</span><br><span>       case GPRS_NS_LL_E1:</span><br><span>          snprintf(buf, buf_len, "e1)");</span><br><span>             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case GPRS_NS_LL_FR:</span><br><span style="color: hsl(120, 100%, 40%);">+           snprintf(buf, buf_len, "fr)netif: %s dlci: %s", "hdlcX", "unsupported");</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span>       default:</span><br><span>             buf[0] = '\0';</span><br><span>               break;</span><br><span>diff --git a/src/gb/gprs_ns2_fr.c b/src/gb/gprs_ns2_fr.c</span><br><span>new file mode 100644</span><br><span>index 0000000..c04a7d1</span><br><span>--- /dev/null</span><br><span>+++ b/src/gb/gprs_ns2_fr.c</span><br><span>@@ -0,0 +1,560 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file gprs_ns2_fr.c</span><br><span style="color: hsl(120, 100%, 40%);">+ * NS-over-FR-over-GRE implementation.</span><br><span style="color: hsl(120, 100%, 40%);">+ * GPRS Networks Service (NS) messages on the Gb interface.</span><br><span style="color: hsl(120, 100%, 40%);">+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)</span><br><span style="color: hsl(120, 100%, 40%);">+ * as well as its successor 3GPP TS 48.016 */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2009-2010,2014,2017 by Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 sysmocom - s.f.m.c. GmbH</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Alexander Couzens <lynxis@fe80.eu></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/socket.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <netinet/in.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <netinet/ip.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <netinet/ip6.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <arpa/inet.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <net/if.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/ioctl.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <netpacket/packet.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <linux/if_ether.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <linux/hdlc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <linux/if.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gprs/frame_relay.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/byteswap.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/select.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/socket.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gprs/gprs_ns2.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "common_vty.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "gprs_ns2_internal.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define GRE_PTYPE_FR 0x6559</span><br><span style="color: hsl(120, 100%, 40%);">+#define GRE_PTYPE_IPv4  0x0800</span><br><span style="color: hsl(120, 100%, 40%);">+#define GRE_PTYPE_IPv6  0x86dd</span><br><span style="color: hsl(120, 100%, 40%);">+#define GRE_PTYPE_KAR   0x0000  /* keepalive response */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef IPPROTO_GRE</span><br><span style="color: hsl(120, 100%, 40%);">+# define IPPROTO_GRE 47</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct gre_hdr {</span><br><span style="color: hsl(120, 100%, 40%);">+  uint16_t flags;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t ptype;</span><br><span style="color: hsl(120, 100%, 40%);">+} __attribute__ ((packed));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__CYGWIN__)</span><br><span style="color: hsl(120, 100%, 40%);">+/**</span><br><span style="color: hsl(120, 100%, 40%);">+ * On BSD the IPv4 struct is called struct ip and instead of iXX</span><br><span style="color: hsl(120, 100%, 40%);">+ * the members are called ip_XX. One could change this code to use</span><br><span style="color: hsl(120, 100%, 40%);">+ * struct ip but that would require to define _BSD_SOURCE and that</span><br><span style="color: hsl(120, 100%, 40%);">+ * might have other complications. Instead make sure struct iphdr</span><br><span style="color: hsl(120, 100%, 40%);">+ * is present on FreeBSD. The below is taken from GLIBC.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The GNU C Library is free software; you can redistribute it and/or</span><br><span style="color: hsl(120, 100%, 40%);">+ * modify it under the terms of the GNU Lesser General Public</span><br><span style="color: hsl(120, 100%, 40%);">+ * License as published by the Free Software Foundation; either</span><br><span style="color: hsl(120, 100%, 40%);">+ * version 2.1 of the License, or (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct iphdr</span><br><span style="color: hsl(120, 100%, 40%);">+  {</span><br><span style="color: hsl(120, 100%, 40%);">+#if BYTE_ORDER == LITTLE_ENDIAN</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int ihl:4;</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int version:4;</span><br><span style="color: hsl(120, 100%, 40%);">+#elif BYTE_ORDER == BIG_ENDIAN</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int version:4;</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int ihl:4;</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int8_t tos;</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int16_t tot_len;</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int16_t id;</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int16_t frag_off;</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int8_t ttl;</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int8_t protocol;</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int16_t check;</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int32_t saddr;</span><br><span style="color: hsl(120, 100%, 40%);">+    u_int32_t daddr;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*The options start here. */</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void free_bind(struct gprs_ns2_vc_bind *bind);</span><br><span style="color: hsl(120, 100%, 40%);">+static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc_driver vc_driver_fr = {</span><br><span style="color: hsl(120, 100%, 40%);">+     .name = "GB frame relay",</span><br><span style="color: hsl(120, 100%, 40%);">+   .free_bind = free_bind,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct priv_bind {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fd fd;</span><br><span style="color: hsl(120, 100%, 40%);">+    char netif[IF_NAMESIZE + 1];</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_fr_link *link;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct priv_vc {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_sockaddr remote;</span><br><span style="color: hsl(120, 100%, 40%);">+  uint16_t dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_fr_dlc *dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void free_vc(struct gprs_ns2_vc *nsvc)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(nsvc);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!nsvc->priv)</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     talloc_free(nsvc->priv);</span><br><span style="color: hsl(120, 100%, 40%);">+   nsvc->priv = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void dump_vty(const struct gprs_ns2_vc_bind *bind, struct vty *vty, bool _stats)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct priv_bind *priv;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gprs_ns2_vc *nsvc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!bind)</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     priv = bind->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       vty_out(vty, "FR bind: %s%s", priv->netif, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_for_each_entry(nsvc, &bind->nsvc, blist) {</span><br><span style="color: hsl(120, 100%, 40%);">+               vty_out(vty, "    %s%s", gprs_ns2_ll_str(nsvc), VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   priv = bind->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */</span><br><span style="color: hsl(120, 100%, 40%);">+static void free_bind(struct gprs_ns2_vc_bind *bind)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct priv_bind *priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!bind)</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     priv = bind->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(llist_empty(&bind->nsvc));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_fr_link_free(priv->link);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_fd_close(&priv->fd);</span><br><span style="color: hsl(120, 100%, 40%);">+      talloc_free(priv);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,</span><br><span style="color: hsl(120, 100%, 40%);">+                               struct gprs_ns2_vc *nsvc,</span><br><span style="color: hsl(120, 100%, 40%);">+                             uint16_t dlci)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct priv_bind *privb = bind->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct priv_vc *priv = talloc_zero(bind, struct priv_vc);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!priv)</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        nsvc->priv = priv;</span><br><span style="color: hsl(120, 100%, 40%);">+ priv->dlci = dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+ priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!priv->dlc) {</span><br><span style="color: hsl(120, 100%, 40%);">+          nsvc->priv = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_free(priv);</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   priv->dlc->rx_cb_data = nsvc;</span><br><span style="color: hsl(120, 100%, 40%);">+   priv->dlc->rx_cb = fr_dlci_rx_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return priv;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,</span><br><span style="color: hsl(120, 100%, 40%);">+                             uint16_t dlci,</span><br><span style="color: hsl(120, 100%, 40%);">+                        struct gprs_ns2_vc **result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gprs_ns2_vc *nsvc;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct priv_vc *vcpriv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!result)</span><br><span style="color: hsl(120, 100%, 40%);">+          return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(nsvc, &bind->nsvc, blist) {</span><br><span style="color: hsl(120, 100%, 40%);">+               vcpriv = nsvc->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+               if (vcpriv->dlci != dlci) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        *result = nsvc;</span><br><span style="color: hsl(120, 100%, 40%);">+                       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* PDU from the network interface towards the fr layer (upwards) */</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_netif_read(struct osmo_fd *bfd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_ns2_vc_bind *bind = bfd->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct priv_bind *priv = bind->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");</span><br><span style="color: hsl(120, 100%, 40%);">+    int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!msg)</span><br><span style="color: hsl(120, 100%, 40%);">+             return -ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = read(bfd->fd, msg->data, NS_ALLOC_SIZE);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DLNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+                goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (rc == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         goto out_err;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msgb_put(msg, rc);</span><br><span style="color: hsl(120, 100%, 40%);">+    msg->dst = priv->link;</span><br><span style="color: hsl(120, 100%, 40%);">+  return osmo_fr_rx(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+out_err:</span><br><span style="color: hsl(120, 100%, 40%);">+   msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* PDU from the frame relay towards the NS-VC (upwards) */</span><br><span style="color: hsl(120, 100%, 40%);">+static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gprs_ns2_vc *nsvc = cb_data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = ns2_recv_vc(nsvc, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_netif_write(struct osmo_fd *bfd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        /* FIXME */</span><br><span style="color: hsl(120, 100%, 40%);">+   return -EIO;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int fr_fd_cb(struct osmo_fd *bfd, unsigned int what)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (what & OSMO_FD_READ)</span><br><span style="color: hsl(120, 100%, 40%);">+          rc = handle_netif_read(bfd);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (what & OSMO_FD_WRITE)</span><br><span style="color: hsl(120, 100%, 40%);">+         rc = handle_netif_write(bfd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! determine if given bind is for FR-GRE encapsulation. */</span><br><span style="color: hsl(120, 100%, 40%);">+int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       return (bind->driver == &vc_driver_fr);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* PDU from the NS-VC towards the frame relay layer (downwards) */</span><br><span style="color: hsl(120, 100%, 40%);">+static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct priv_vc *vcpriv = nsvc->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     msg->dst = vcpriv->dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+ return osmo_fr_tx_dlc(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* PDU from the frame relay layer towards the network interface (downwards) */</span><br><span style="color: hsl(120, 100%, 40%);">+int fr_tx_cb(void *data, struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gprs_ns2_vc_bind *bind = data;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct priv_bind *priv = bind->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* FIXME half writes */</span><br><span style="color: hsl(120, 100%, 40%);">+       rc = write(priv->fd.fd, msg->data, msg->len);</span><br><span style="color: hsl(120, 100%, 40%);">+        msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int devname2ifindex(const char *ifname)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ifreq ifr;</span><br><span style="color: hsl(120, 100%, 40%);">+     int sk, rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sk < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return sk;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        memset(&ifr, 0, sizeof(ifr));</span><br><span style="color: hsl(120, 100%, 40%);">+     strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));</span><br><span style="color: hsl(120, 100%, 40%);">+  ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = ioctl(sk, SIOCGIFINDEX, &ifr);</span><br><span style="color: hsl(120, 100%, 40%);">+       close(sk);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return ifr.ifr_ifindex;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int open_socket(const char *ifname)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct sockaddr_ll addr;</span><br><span style="color: hsl(120, 100%, 40%);">+      int ifindex;</span><br><span style="color: hsl(120, 100%, 40%);">+  int fd, rc, on = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ifindex = devname2ifindex(ifname);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ifindex < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         LOGP(DLNS, LOGL_ERROR, "Can not get interface index for interface %s\n", ifname);</span><br><span style="color: hsl(120, 100%, 40%);">+           return ifindex;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   memset(&addr, 0, sizeof(addr));</span><br><span style="color: hsl(120, 100%, 40%);">+   addr.sll_family = AF_PACKET;</span><br><span style="color: hsl(120, 100%, 40%);">+  addr.sll_protocol = htons(ETH_P_ALL);</span><br><span style="color: hsl(120, 100%, 40%);">+ addr.sll_ifindex = ifindex;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));</span><br><span style="color: hsl(120, 100%, 40%);">+   if (fd < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DLNS, LOGL_ERROR, "Can not get socket for interface %s. Are you root or have CAP_RAW_SOCKET?\n", ifname);</span><br><span style="color: hsl(120, 100%, 40%);">+              return fd;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ioctl(fd, FIONBIO, (unsigned char *)&on) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGP(DLGLOBAL, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                    "cannot set this socket unblocking: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+             close(fd);</span><br><span style="color: hsl(120, 100%, 40%);">+            fd = -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));</span><br><span style="color: hsl(120, 100%, 40%);">+    if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DLNS, LOGL_ERROR, "Can not bind for interface %s\n", ifname);</span><br><span style="color: hsl(120, 100%, 40%);">+          close(fd);</span><br><span style="color: hsl(120, 100%, 40%);">+            return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return fd;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Create a new bind for NS over FR.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] nsi NS instance in which to create the bind</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] netif Network interface to bind to</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] fr_network</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] fr_role</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[out] result pointer to created bind</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \return 0 on success; negative on error */</span><br><span style="color: hsl(120, 100%, 40%);">+int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const char *netif,</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct osmo_fr_network *fr_network,</span><br><span style="color: hsl(120, 100%, 40%);">+                   enum osmo_fr_role fr_role,</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct gprs_ns2_vc_bind **result)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_ns2_vc_bind *bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);</span><br><span style="color: hsl(120, 100%, 40%);">+    struct priv_bind *priv;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_fr_link *fr_link;</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!bind)</span><br><span style="color: hsl(120, 100%, 40%);">+            return -ENOSPC;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     bind->driver = &vc_driver_fr;</span><br><span style="color: hsl(120, 100%, 40%);">+  bind->send_vc = fr_vc_sendmsg;</span><br><span style="color: hsl(120, 100%, 40%);">+     bind->free_vc = free_vc;</span><br><span style="color: hsl(120, 100%, 40%);">+   bind->dump_vty = dump_vty;</span><br><span style="color: hsl(120, 100%, 40%);">+ bind->nsi = nsi;</span><br><span style="color: hsl(120, 100%, 40%);">+   priv = bind->priv = talloc_zero(bind, struct priv_bind);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!priv) {</span><br><span style="color: hsl(120, 100%, 40%);">+          rc = -ENOSPC;</span><br><span style="color: hsl(120, 100%, 40%);">+         goto err_bind;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   priv->fd.cb = fr_fd_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+    priv->fd.data = bind;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (strlen(netif) > IF_NAMESIZE) {</span><br><span style="color: hsl(120, 100%, 40%);">+         rc = -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+         goto err_priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     strncpy(priv->netif, netif, sizeof(priv->netif));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ns2_vty_bind_apply(bind);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (result)</span><br><span style="color: hsl(120, 100%, 40%);">+           *result = bind;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* FIXME: move fd handling into socket.c */</span><br><span style="color: hsl(120, 100%, 40%);">+   fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!fr_link) {</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+         goto err_priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   fr_link->tx_cb = fr_tx_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+ fr_link->tx_cb_data = bind;</span><br><span style="color: hsl(120, 100%, 40%);">+        priv->link = fr_link;</span><br><span style="color: hsl(120, 100%, 40%);">+      priv->fd.fd = rc = open_socket(netif);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                goto err_fr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        priv->fd.when = OSMO_FD_READ;</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = osmo_fd_register(&priv->fd);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                goto err_fd;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        INIT_LLIST_HEAD(&bind->nsvc);</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_add(&bind->list, &nsi->binding);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+err_fd:</span><br><span style="color: hsl(120, 100%, 40%);">+ close(priv->fd.fd);</span><br><span style="color: hsl(120, 100%, 40%);">+err_fr:</span><br><span style="color: hsl(120, 100%, 40%);">+       osmo_fr_link_free(fr_link);</span><br><span style="color: hsl(120, 100%, 40%);">+err_priv:</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(priv);</span><br><span style="color: hsl(120, 100%, 40%);">+err_bind:</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(bind);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return the network interface of the bind</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] bind The bind</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return the network interface</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct priv_bind *priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     priv = bind->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+ return priv->netif;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Find NS bind for a given network interface</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] nsi NS instance</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] netif the network interface to search for</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return the bind or NULL if not found</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(</span><br><span style="color: hsl(120, 100%, 40%);">+               struct gprs_ns2_inst *nsi,</span><br><span style="color: hsl(120, 100%, 40%);">+            const char *netif)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct gprs_ns2_vc_bind *bind;</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *_netif;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(nsi);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(netif);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(bind, &nsi->binding, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!gprs_ns2_is_fr_bind(bind))</span><br><span style="color: hsl(120, 100%, 40%);">+                       continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           _netif = gprs_ns2_fr_bind_netif(bind);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!strncmp(_netif, netif, IF_NAMESIZE))</span><br><span style="color: hsl(120, 100%, 40%);">+                     return bind;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Create, connect and activate a new FR-based NS-VC</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] bind bind in which the new NS-VC is to be created</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] dlci Data Link connection identifier</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       uint16_t nsei,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        uint16_t nsvci,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       uint16_t dlci)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     bool created_nse = false;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_ns2_vc *nsvc = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct priv_vc *priv = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!nse) {</span><br><span style="color: hsl(120, 100%, 40%);">+           nse = gprs_ns2_create_nse(bind->nsi, nsei);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!nse)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          created_nse = true;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (nsvc) {</span><br><span style="color: hsl(120, 100%, 40%);">+           goto err_nse;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   nsvc = ns2_vc_alloc(bind, nse, true);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!nsvc)</span><br><span style="color: hsl(120, 100%, 40%);">+            goto err_nse;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!priv)</span><br><span style="color: hsl(120, 100%, 40%);">+            goto err;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   nsvc->nsvci = nsvci;</span><br><span style="color: hsl(120, 100%, 40%);">+       nsvc->nsvci_is_valid = true;</span><br><span style="color: hsl(120, 100%, 40%);">+       nsvc->ll = GPRS_NS_LL_FR;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        gprs_ns2_vc_fsm_start(nsvc);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return nsvc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+err:</span><br><span style="color: hsl(120, 100%, 40%);">+  gprs_ns2_free_nsvc(nsvc);</span><br><span style="color: hsl(120, 100%, 40%);">+err_nse:</span><br><span style="color: hsl(120, 100%, 40%);">+   if (created_nse)</span><br><span style="color: hsl(120, 100%, 40%);">+              gprs_ns2_free_nse(nse);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gprs_ns2_fr_nsvc_by_dlci</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] bind</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] dlci Data Link connection identifier</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return the nsvc or NULL if not found</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        uint16_t dlci)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_ns2_vc *nsvc;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct priv_vc *vcpriv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(nsvc, &bind->nsvc, blist) {</span><br><span style="color: hsl(120, 100%, 40%);">+               vcpriv = nsvc->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             if (dlci == vcpriv->dlci)</span><br><span style="color: hsl(120, 100%, 40%);">+                  return nsvc;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h</span><br><span>index dee3ab7..e4d2d8a 100644</span><br><span>--- a/src/gb/gprs_ns2_internal.h</span><br><span>+++ b/src/gb/gprs_ns2_internal.h</span><br><span>@@ -60,6 +60,7 @@</span><br><span> enum gprs_ns_ll {</span><br><span>        GPRS_NS_LL_UDP,         /*!< NS/UDP/IP */</span><br><span>         GPRS_NS_LL_E1,          /*!< NS/E1 */</span><br><span style="color: hsl(120, 100%, 40%);">+      GPRS_NS_LL_FR,          /*!< NS/FR */</span><br><span>     GPRS_NS_LL_FR_GRE,      /*!< NS/FR/GRE/IP */</span><br><span> };</span><br><span> </span><br><span>diff --git a/src/gb/gprs_ns2_vty.c b/src/gb/gprs_ns2_vty.c</span><br><span>index 65fe88e..174f952 100644</span><br><span>--- a/src/gb/gprs_ns2_vty.c</span><br><span>+++ b/src/gb/gprs_ns2_vty.c</span><br><span>@@ -31,6 +31,7 @@</span><br><span> #include <stdint.h></span><br><span> </span><br><span> #include <arpa/inet.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <net/if.h></span><br><span> </span><br><span> #include <osmocom/core/msgb.h></span><br><span> #include <osmocom/core/byteswap.h></span><br><span>@@ -41,6 +42,7 @@</span><br><span> #include <osmocom/core/sockaddr_str.h></span><br><span> #include <osmocom/core/linuxlist.h></span><br><span> #include <osmocom/core/socket.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gprs/frame_relay.h></span><br><span> #include <osmocom/gprs/gprs_ns2.h></span><br><span> #include <osmocom/gsm/tlv.h></span><br><span> #include <osmocom/vty/vty.h></span><br><span>@@ -77,12 +79,19 @@</span><br><span>     uint16_t nsvci;</span><br><span>      uint16_t frdlci;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+  struct {</span><br><span style="color: hsl(120, 100%, 40%);">+              enum osmo_fr_role role;</span><br><span style="color: hsl(120, 100%, 40%);">+       } fr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       char netif[IF_NAMESIZE + 1];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       bool remote_end_is_sgsn;</span><br><span>     bool configured;</span><br><span> };</span><br><span> </span><br><span> static struct gprs_ns2_inst *vty_nsi = NULL;</span><br><span> static struct ns2_vty_priv priv;</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fr_network *vty_fr_network = NULL;</span><br><span> </span><br><span> /* FIXME: this should go to some common file as it is copied</span><br><span>  * in vty_interface.c of the BSC */</span><br><span>@@ -222,6 +231,11 @@</span><br><span>                              vtyvc->nsei, vtyvc->frdlci,</span><br><span>                            VTY_NEWLINE);</span><br><span>                        break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case GPRS_NS_LL_FR:</span><br><span style="color: hsl(120, 100%, 40%);">+                   vty_out(vty, " nse %u fr %s dlci %u%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                             vtyvc->nsei, vtyvc->netif, vtyvc->frdlci,</span><br><span style="color: hsl(120, 100%, 40%);">+                            VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+                 break;</span><br><span>               default:</span><br><span>                     break;</span><br><span>               }</span><br><span>@@ -356,6 +370,44 @@</span><br><span> </span><br><span> #define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_nse_fr, cfg_nse_fr_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+       "nse <0-65535> nsvci <0-65535> (fr|frnet) NETIF dlci <0-1023>",</span><br><span style="color: hsl(120, 100%, 40%);">+   NSE_CMD_STR</span><br><span style="color: hsl(120, 100%, 40%);">+   "NS Virtual Connection\n"</span><br><span style="color: hsl(120, 100%, 40%);">+   "NS Virtual Connection ID (NSVCI)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        "frame relay\n"</span><br><span style="color: hsl(120, 100%, 40%);">+     IFNAME_STR</span><br><span style="color: hsl(120, 100%, 40%);">+    "Data Link connection identifier\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Data Link connection identifier\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ )</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ns2_vty_vc *vtyvc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   uint16_t nsei = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+        uint16_t nsvci = atoi(argv[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *role = argv[2];</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *name = argv[3];</span><br><span style="color: hsl(120, 100%, 40%);">+   uint16_t dlci = atoi(argv[4]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      vtyvc = vtyvc_by_nsei(nsei, true);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!vtyvc) {</span><br><span style="color: hsl(120, 100%, 40%);">+         vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+             return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!strcmp(role, "fr"))</span><br><span style="color: hsl(120, 100%, 40%);">+            vtyvc->fr.role = FR_ROLE_USER_EQUIPMENT;</span><br><span style="color: hsl(120, 100%, 40%);">+   else if (!strcmp(role, "frnet"))</span><br><span style="color: hsl(120, 100%, 40%);">+            vtyvc->fr.role = FR_ROLE_NETWORK_EQUIPMENT;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_strlcpy(vtyvc->netif, name, sizeof(vtyvc->netif));</span><br><span style="color: hsl(120, 100%, 40%);">+ vtyvc->frdlci = dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+      vtyvc->nsvci = nsvci;</span><br><span style="color: hsl(120, 100%, 40%);">+      vtyvc->ll = GPRS_NS_LL_FR;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,</span><br><span>   "nse <0-65535> nsvci <0-65535>",</span><br><span>       NSE_CMD_STR</span><br><span>@@ -428,7 +480,8 @@</span><br><span>    "Frame Relay DLCI Number\n")</span><br><span> {</span><br><span>  uint16_t nsei = atoi(argv[0]);</span><br><span style="color: hsl(0, 100%, 40%);">-  uint16_t dlci = atoi(argv[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+        uint16_t nsvci = atoi(argv[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t dlci = atoi(argv[2]);</span><br><span>       struct ns2_vty_vc *vtyvc;</span><br><span> </span><br><span>        vtyvc = vtyvc_by_nsei(nsei, true);</span><br><span>@@ -443,6 +496,7 @@</span><br><span>     }</span><br><span> </span><br><span>        vtyvc->frdlci = dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+      vtyvc->nsvci = nsvci;</span><br><span> </span><br><span>         return CMD_SUCCESS;</span><br><span> }</span><br><span>@@ -703,6 +757,7 @@</span><br><span> </span><br><span>   install_lib_element(CONFIG_NODE, &cfg_ns_cmd);</span><br><span>   install_node(&ns_node, config_write_ns);</span><br><span style="color: hsl(120, 100%, 40%);">+  install_lib_element(L_NS_NODE, &cfg_nse_fr_cmd);</span><br><span>         install_lib_element(L_NS_NODE, &cfg_nse_nsvci_cmd);</span><br><span>      install_lib_element(L_NS_NODE, &cfg_nse_remoteip_cmd);</span><br><span>   install_lib_element(L_NS_NODE, &cfg_nse_remoteport_cmd);</span><br><span>@@ -730,10 +785,11 @@</span><br><span>  */</span><br><span> int gprs_ns2_vty_create() {</span><br><span>   struct ns2_vty_vc *vtyvc;</span><br><span style="color: hsl(0, 100%, 40%);">-       struct gprs_ns2_vc_bind *bind;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_ns2_vc_bind *bind, *fr;</span><br><span>  struct gprs_ns2_nse *nse;</span><br><span>    struct gprs_ns2_vc *nsvc;</span><br><span>    struct osmo_sockaddr sockaddr;</span><br><span style="color: hsl(120, 100%, 40%);">+        int rc = 0;</span><br><span> </span><br><span>      if (!vty_nsi)</span><br><span>                return -1;</span><br><span>@@ -754,18 +810,28 @@</span><br><span> </span><br><span>       /* create vcs */</span><br><span>     llist_for_each_entry(vtyvc, &priv.vtyvc, list) {</span><br><span style="color: hsl(0, 100%, 40%);">-            if (strlen(vtyvc->remote.ip) == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                 /* Invalid IP for VC */</span><br><span style="color: hsl(0, 100%, 40%);">-                 continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(120, 100%, 40%);">+             /* validate settings */</span><br><span style="color: hsl(120, 100%, 40%);">+               switch (vtyvc->ll) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GPRS_NS_LL_UDP:</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (strlen(vtyvc->remote.ip) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               /* Invalid IP for VC */</span><br><span style="color: hsl(120, 100%, 40%);">+                               continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           if (!vtyvc->remote.port) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   /* Invalid port for VC */</span><br><span style="color: hsl(0, 100%, 40%);">-                       continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (!vtyvc->remote.port) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         /* Invalid port for VC */</span><br><span style="color: hsl(120, 100%, 40%);">+                             continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        /* Invalid sockaddr for VC */</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              /* Invalid sockaddr for VC */</span><br><span style="color: hsl(120, 100%, 40%);">+                         continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case GPRS_NS_LL_FR:</span><br><span style="color: hsl(120, 100%, 40%);">+                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case GPRS_NS_LL_FR_GRE:</span><br><span style="color: hsl(120, 100%, 40%);">+               case GPRS_NS_LL_E1:</span><br><span>                  continue;</span><br><span>            }</span><br><span> </span><br><span>@@ -779,15 +845,46 @@</span><br><span>                }</span><br><span>            nse->persistent = true;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-          nsvc = gprs_ns2_ip_connect(bind,</span><br><span style="color: hsl(0, 100%, 40%);">-                                           &sockaddr,</span><br><span style="color: hsl(0, 100%, 40%);">-                                          nse,</span><br><span style="color: hsl(0, 100%, 40%);">-                                    vtyvc->nsvci);</span><br><span style="color: hsl(0, 100%, 40%);">-            if (!nsvc) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    /* Could not create NSVC, connect failed */</span><br><span style="color: hsl(120, 100%, 40%);">+           switch (vtyvc->ll) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GPRS_NS_LL_UDP:</span><br><span style="color: hsl(120, 100%, 40%);">+                  nsvc = gprs_ns2_ip_connect(bind,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 &sockaddr,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                nse,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  vtyvc->nsvci);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (!nsvc) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          /* Could not create NSVC, connect failed */</span><br><span style="color: hsl(120, 100%, 40%);">+                           continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     nsvc->persistent = true;</span><br><span style="color: hsl(120, 100%, 40%);">+                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case GPRS_NS_LL_FR: {</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (vty_fr_network == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         /* TODO: add a switch for BSS/SGSN/gbproxy */</span><br><span style="color: hsl(120, 100%, 40%);">+                         vty_fr_network = osmo_fr_network_alloc(vty_nsi);</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+                     fr = gprs_ns2_fr_bind_by_netif(</span><br><span style="color: hsl(120, 100%, 40%);">+                                               vty_nsi,</span><br><span style="color: hsl(120, 100%, 40%);">+                                              vtyvc->netif);</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (!fr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            rc = gprs_ns2_fr_bind(vty_nsi, vtyvc->netif, vty_fr_network, vtyvc->fr.role, &fr);</span><br><span style="color: hsl(120, 100%, 40%);">+                          if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      LOGP(DLNS, LOGL_ERROR, "Can not create fr bind on device %s err: %d\n", vtyvc->netif, rc);</span><br><span style="color: hsl(120, 100%, 40%);">+                                       return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+                            }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   nsvc = gprs_ns2_fr_connect(fr, vtyvc->nsei, vtyvc->nsvci, vtyvc->frdlci);</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!nsvc) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          /* Could not create NSVC, connect failed */</span><br><span style="color: hsl(120, 100%, 40%);">+                           continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     nsvc->persistent = true;</span><br><span style="color: hsl(120, 100%, 40%);">+                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             case GPRS_NS_LL_FR_GRE:</span><br><span style="color: hsl(120, 100%, 40%);">+               case GPRS_NS_LL_E1:</span><br><span>                  continue;</span><br><span>            }</span><br><span style="color: hsl(0, 100%, 40%);">-               nsvc->persistent = true;</span><br><span>  }</span><br><span> </span><br><span> </span><br><span>diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map</span><br><span>index 72437ab..6a334aa 100644</span><br><span>--- a/src/gb/libosmogb.map</span><br><span>+++ b/src/gb/libosmogb.map</span><br><span>@@ -45,6 +45,14 @@</span><br><span> bssgp_vty_init;</span><br><span> bssgp_nsi;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+osmo_fr_network_alloc;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_fr_link_alloc;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_fr_link_free;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_fr_dlc_alloc;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_fr_rx;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_fr_tx_dlc;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_fr_role_names;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> gprs_ns_signal_ns_names;</span><br><span> gprs_ns_pdu_strings;</span><br><span> gprs_ns_cause_str;</span><br><span>@@ -86,6 +94,13 @@</span><br><span> gprs_ns2_free_nses;</span><br><span> gprs_ns2_free_nsvc;</span><br><span> gprs_ns2_frgre_bind;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_fr_bind;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_fr_bind_netif;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_fr_bind_by_netif;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_fr_connect;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_fr_nsvc_by_dlci;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_is_fr_bind;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_find_vc_by_dlci;</span><br><span> gprs_ns2_instantiate;</span><br><span> gprs_ns2_ip_bind;</span><br><span> gprs_ns2_ip_bind_by_sockaddr;</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/21243">change 21243</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/21243"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Id3b49f93d33c271f77cd9c9db03cde6b727a4d30 </div>
<div style="display:none"> Gerrit-Change-Number: 21243 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: lynxis lazus <lynxis@fe80.eu> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>