<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>