aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac13
-rwxr-xr-xcontrib/jenkins_arm.sh1
-rw-r--r--contrib/libosmocore.spec.in1
-rw-r--r--debian/control1
-rw-r--r--include/Makefile.am2
-rw-r--r--include/osmocom/core/logging.h22
-rw-r--r--include/osmocom/core/mnl.h22
-rw-r--r--include/osmocom/core/serial.h1
-rw-r--r--include/osmocom/core/tdef.h1
-rw-r--r--include/osmocom/gprs/frame_relay.h136
-rw-r--r--include/osmocom/gprs/gprs_bssgp.h5
-rw-r--r--include/osmocom/gprs/gprs_ns2.h60
-rw-r--r--include/osmocom/gprs/protocol/gsm_08_18.h130
-rw-r--r--include/osmocom/gsm/bts_features.h1
-rw-r--r--include/osmocom/gsm/gsm48_ie.h4
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_08.h172
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_08_gprs.h1
-rw-r--r--include/osmocom/gsm/protocol/gsm_08_58.h21
-rw-r--r--include/osmocom/vty/command.h5
-rw-r--r--include/osmocom/vty/vty.h3
-rw-r--r--src/Makefile.am7
-rw-r--r--src/bitvec.c2
-rw-r--r--src/gb/Makefile.am9
-rw-r--r--src/gb/common_vty.c8
-rw-r--r--src/gb/frame_relay.c966
-rw-r--r--src/gb/gprs_bssgp.c86
-rw-r--r--src/gb/gprs_bssgp_bss.c39
-rw-r--r--src/gb/gprs_bssgp_util.c19
-rw-r--r--src/gb/gprs_ns2.c266
-rw-r--r--src/gb/gprs_ns2_fr.c669
-rw-r--r--src/gb/gprs_ns2_frgre.c1
-rw-r--r--src/gb/gprs_ns2_internal.h29
-rw-r--r--src/gb/gprs_ns2_message.c35
-rw-r--r--src/gb/gprs_ns2_sns.c7
-rw-r--r--src/gb/gprs_ns2_udp.c23
-rw-r--r--src/gb/gprs_ns2_vc_fsm.c160
-rw-r--r--src/gb/gprs_ns2_vty.c340
-rw-r--r--src/gb/libosmogb.map23
-rw-r--r--src/gsm/bts_features.c1
-rw-r--r--src/gsm/gsm0411_smr.c2
-rw-r--r--src/gsm/gsm48.c5
-rw-r--r--src/gsm/gsm48_ie.c246
-rw-r--r--src/gsm/gsm_04_08_gprs.c4
-rw-r--r--src/gsm/libosmogsm.map1
-rw-r--r--src/gsm/rsl.c1
-rw-r--r--src/mnl.c116
-rw-r--r--src/serial.c52
-rw-r--r--src/sim/card_fs_usim.c68
-rw-r--r--src/stats_statsd.c9
-rw-r--r--src/tdef.c12
-rw-r--r--src/vty/command.c63
-rw-r--r--src/vty/cpu_sched_vty.c10
-rw-r--r--tests/gsm0408/gsm0408_test.c198
-rw-r--r--tests/gsm0408/gsm0408_test.ok342
-rw-r--r--tests/tdef/tdef_test.c12
-rw-r--r--tests/tdef/tdef_test.ok42
56 files changed, 4119 insertions, 356 deletions
diff --git a/configure.ac b/configure.ac
index 7de495bc..10fb4963 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,19 @@ AS_IF([test "x$systemd_logging" = "xyes"], [
AM_CONDITIONAL(ENABLE_SYSTEMD_LOGGING, test "x$systemd_logging" = "xyes")
AC_SUBST(ENABLE_SYSTEMD_LOGGING)
+AC_ARG_ENABLE([libmnl],
+ [AS_HELP_STRING(
+ [--disable-libmnl],
+ [Build without netlink socket support via libmnl]
+ )],
+ [mnl=$enableval], [mnl="yes"])
+AS_IF([test "x$mnl" = "xyes"], [
+ PKG_CHECK_MODULES(LIBMNL, libmnl)
+ AC_DEFINE([ENABLE_LIBMNL], [1], [Enable netlink socket support via libmnl])
+])
+AM_CONDITIONAL(ENABLE_LIBMNL, test "x$mnl" = "xyes")
+AC_SUBST(ENABLE_LIBMNL)
+
AC_ARG_ENABLE([libsctp], [AS_HELP_STRING([--disable-libsctp], [Do not enable socket multiaddr APIs requiring libsctp])],
[ENABLE_LIBSCTP=$enableval], [ENABLE_LIBSCTP="yes"])
AM_CONDITIONAL(ENABLE_LIBSCTP, test x"$ENABLE_LIBSCTP" = x"yes")
diff --git a/contrib/jenkins_arm.sh b/contrib/jenkins_arm.sh
index c9cd922e..e7df37d1 100755
--- a/contrib/jenkins_arm.sh
+++ b/contrib/jenkins_arm.sh
@@ -20,6 +20,7 @@ build() {
--disable-shared \
--disable-libsctp \
--disable-libusb \
+ --disable-libmnl \
--enable-external-tests \
CFLAGS="-Os -ffunction-sections -fdata-sections -nostartfiles -nodefaultlibs $WERROR_FLAGS"
diff --git a/contrib/libosmocore.spec.in b/contrib/libosmocore.spec.in
index fb45516e..728a0fb0 100644
--- a/contrib/libosmocore.spec.in
+++ b/contrib/libosmocore.spec.in
@@ -30,6 +30,7 @@ BuildRequires: pkgconfig(gnutls) >= 2.12.0
BuildRequires: pkgconfig(libpcsclite)
BuildRequires: pkgconfig(libusb-1.0)
BuildRequires: pkgconfig(talloc) >= 2.0.1
+BuildRequires: pkgconfig(libmnl)
%description
libosmocore is a package with various utility functions that were
diff --git a/debian/control b/debian/control
index 381e2914..24e729ae 100644
--- a/debian/control
+++ b/debian/control
@@ -17,6 +17,7 @@ Build-Depends: debhelper (>= 9),
libtalloc-dev,
libsctp-dev,
libusb-1.0-0-dev,
+ libmnl-dev,
python3:native
Standards-Version: 3.9.8
Vcs-Git: git://git.osmocom.org/libosmocore.git
diff --git a/include/Makefile.am b/include/Makefile.am
index 44ff3789..71171a48 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -32,6 +32,7 @@ nobase_include_HEADERS = \
osmocom/core/linuxrbtree.h \
osmocom/core/logging.h \
osmocom/core/loggingrb.h \
+ osmocom/core/mnl.h \
osmocom/core/stats.h \
osmocom/core/macaddr.h \
osmocom/core/msgb.h \
@@ -60,6 +61,7 @@ nobase_include_HEADERS = \
osmocom/ctrl/control_cmd.h \
osmocom/ctrl/control_if.h \
osmocom/ctrl/ports.h \
+ osmocom/gprs/frame_relay.h \
osmocom/gprs/gprs_bssgp.h \
osmocom/gprs/gprs_bssgp_bss.h \
osmocom/gprs/gprs_msgb.h \
diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h
index 6d0d5a3a..418a42ec 100644
--- a/include/osmocom/core/logging.h
+++ b/include/osmocom/core/logging.h
@@ -11,11 +11,6 @@
#include <osmocom/core/defs.h>
#include <osmocom/core/linuxlist.h>
-/*! Maximum number of logging contexts */
-#define LOG_MAX_CTX 8
-/*! Maximum number of logging filters */
-#define LOG_MAX_FILTERS 8
-
#ifndef DEBUG
#define DEBUG
#endif
@@ -162,11 +157,6 @@ struct log_info_cat {
uint8_t enabled; /*!< is this category enabled or not */
};
-/*! Log context information, passed to filter */
-struct log_context {
- void *ctx[LOG_MAX_CTX+1];
-};
-
/*! Indexes to indicate the object currently acted upon.
* Array indexes for the global \a log_context array. */
enum log_ctx_index {
@@ -175,6 +165,7 @@ enum log_ctx_index {
LOG_CTX_BSC_SUBSCR,
LOG_CTX_VLR_SUBSCR,
LOG_CTX_L1_SAPI,
+ LOG_CTX_GB_NSE,
_LOG_CTX_COUNT
};
@@ -188,9 +179,20 @@ enum log_filter_index {
LOG_FLT_BSC_SUBSCR,
LOG_FLT_VLR_SUBSCR,
LOG_FLT_L1_SAPI,
+ LOG_FLT_GB_NSE,
_LOG_FLT_COUNT
};
+/*! Maximum number of logging contexts */
+#define LOG_MAX_CTX _LOG_CTX_COUNT
+/*! Maximum number of logging filters */
+#define LOG_MAX_FILTERS _LOG_FLT_COUNT
+
+/*! Log context information, passed to filter */
+struct log_context {
+ void *ctx[LOG_MAX_CTX+1];
+};
+
/*! Compatibility with older libosmocore versions */
#define LOG_FILTER_ALL (1<<LOG_FLT_ALL)
/*! Compatibility with older libosmocore versions */
diff --git a/include/osmocom/core/mnl.h b/include/osmocom/core/mnl.h
new file mode 100644
index 00000000..11c83530
--- /dev/null
+++ b/include/osmocom/core/mnl.h
@@ -0,0 +1,22 @@
+/*! \file select.h
+ * libmnl integration
+ */
+#pragma once
+
+#include <osmocom/core/select.h>
+#include <libmnl/libmnl.h>
+
+/*! osmocom wrapper around libmnl abstraction of netlink socket */
+struct osmo_mnl {
+ /*! osmo-wrapped netlink file descriptor */
+ struct osmo_fd ofd;
+ /*! libmnl socket abstraction */
+ struct mnl_socket *mnls;
+ /*! call-back called for received netlink messages */
+ mnl_cb_t mnl_cb;
+ /*! opaque data provided by user */
+ void *priv;
+};
+
+struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv);
+void osmo_mnl_destroy(struct osmo_mnl *omnl);
diff --git a/include/osmocom/core/serial.h b/include/osmocom/core/serial.h
index 39614a47..443275f2 100644
--- a/include/osmocom/core/serial.h
+++ b/include/osmocom/core/serial.h
@@ -32,5 +32,6 @@ int osmo_serial_init(const char *dev, speed_t baudrate);
int osmo_serial_set_baudrate(int fd, speed_t baudrate);
int osmo_serial_set_custom_baudrate(int fd, int baudrate);
int osmo_serial_clear_custom_baudrate(int fd);
+int osmo_serial_speed_t(unsigned int baudrate, speed_t *speed);
/*! @} */
diff --git a/include/osmocom/core/tdef.h b/include/osmocom/core/tdef.h
index 54819d95..627ba3f9 100644
--- a/include/osmocom/core/tdef.h
+++ b/include/osmocom/core/tdef.h
@@ -40,6 +40,7 @@ enum osmo_tdef_unit {
OSMO_TDEF_MS, /*!< milliseconds */
OSMO_TDEF_M, /*!< minutes */
OSMO_TDEF_CUSTOM, /*!< unspecified unit, explained in osmo_tdef.desc. */
+ OSMO_TDEF_US, /*!< microseconds */
};
extern const struct value_string osmo_tdef_unit_names[];
diff --git a/include/osmocom/gprs/frame_relay.h b/include/osmocom/gprs/frame_relay.h
new file mode 100644
index 00000000..2860c6be
--- /dev/null
+++ b/include/osmocom/gprs/frame_relay.h
@@ -0,0 +1,136 @@
+/*! \file frame_relay.h */
+
+/* (C) 2020 Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/utils.h>
+
+#include <stdint.h>
+
+struct osmo_tdef;
+struct msgb;
+
+enum osmo_fr_role {
+ FR_ROLE_USER_EQUIPMENT,
+ FR_ROLE_NETWORK_EQUIPMENT,
+};
+
+extern const struct value_string osmo_fr_role_names[];
+
+static inline const char *osmo_fr_role_str(enum osmo_fr_role role) {
+ return get_value_string(osmo_fr_role_names, role);
+}
+
+struct osmo_fr_network {
+ struct llist_head links;
+
+ unsigned int n391; /* full status polling counter */
+ unsigned int n392; /* error threshold */
+ unsigned int n393; /* monitored events count */
+
+ struct osmo_tdef *T_defs; /* T391, T392 */
+};
+
+struct osmo_fr_dlc;
+
+/* Frame Relay Link */
+struct osmo_fr_link {
+ /* list in osmo_fr_network.links */
+ struct llist_head list;
+ struct osmo_fr_network *net;
+ enum osmo_fr_role role;
+ /* human-readable name */
+ const char *name;
+
+ /* value of the last received send sequence number field in the
+ * link integrity verification information element */
+ uint8_t last_rx_seq;
+
+ /* value of the send sequence number field of the last link
+ * integrity verification information element sent */
+ uint8_t last_tx_seq;
+
+ struct osmo_timer_list t391;
+ struct osmo_timer_list t392;
+
+ unsigned int polling_count;
+ unsigned int err_count;
+ unsigned int succeed;
+ /* the type of the last status enquiry */
+ uint8_t expected_rep;
+ bool state;
+
+ /* list of data link connections at this link */
+ struct llist_head dlc_list;
+
+ int (*unknown_dlc_rx_cb)(void *cb_data, struct msgb *msg);
+ void *unknown_dlc_rx_cb_data;
+
+ int (*tx_cb)(void *data, struct msgb *msg);
+ void *tx_cb_data;
+};
+
+/* Frame Relay Data Link Connection */
+struct osmo_fr_dlc {
+ /* entry in fr_link.dlc_list */
+ struct llist_head list;
+ struct osmo_fr_link *link;
+
+ uint16_t dlci;
+
+ /* is this DLC marked active for traffic? */
+ bool active;
+ /* was this DLC newly added? */
+ bool add;
+ /* is this DLC about to be destroyed */
+ bool del;
+
+ /* The local state needs to be transferred to the USER;
+ * NET must wait until USER confirms it implicitly by a seq number check */
+ bool state_send;
+
+ int (*rx_cb)(void *cb_data, struct msgb *msg);
+ void *rx_cb_data;
+};
+
+/* allocate a frame relay network */
+struct osmo_fr_network *osmo_fr_network_alloc(void *ctx);
+
+/* allocate a frame relay link in a given network */
+struct osmo_fr_link *osmo_fr_link_alloc(struct osmo_fr_network *net, enum osmo_fr_role role, const char *name);
+
+/* free a frame link in a given network */
+void osmo_fr_link_free(struct osmo_fr_link *link);
+
+/* allocate a data link connectoin on a given framerelay link */
+struct osmo_fr_dlc *osmo_fr_dlc_alloc(struct osmo_fr_link *link, uint16_t dlci);
+void osmo_fr_dlc_free(struct osmo_fr_dlc *dlc);
+
+struct osmo_fr_dlc *osmo_fr_dlc_by_dlci(struct osmo_fr_link *link, uint16_t dlci);
+
+int osmo_fr_rx(struct msgb *msg);
+int osmo_fr_tx_dlc(struct msgb *msg);
diff --git a/include/osmocom/gprs/gprs_bssgp.h b/include/osmocom/gprs/gprs_bssgp.h
index b9d251cc..e962b444 100644
--- a/include/osmocom/gprs/gprs_bssgp.h
+++ b/include/osmocom/gprs/gprs_bssgp.h
@@ -20,6 +20,7 @@ struct msgb *bssgp_msgb_alloc(void);
struct msgb *bssgp_msgb_copy(const struct msgb *msg, const char *name);
const char *bssgp_cause_str(enum gprs_bssgp_cause cause);
const char *bssgp_pdu_str(enum bssgp_pdu_type pdu);
+int bssgp_tx_bvc_reset_nsei_bvci(uint16_t nsei, uint16_t bvci, enum gprs_bssgp_cause cause, const struct gprs_ra_id *ra_id, uint16_t cell_id);
/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */
int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
uint16_t bvci, uint16_t ns_bvci);
@@ -111,6 +112,8 @@ struct bssgp_bvc_ctx {
//struct gprs_nsvc *nsvc;
};
extern struct llist_head bssgp_bvc_ctxts;
+/* Create a BTS Context with BVCI+NSEI */
+struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
/* Find a BTS Context based on parsed RA ID and Cell ID */
struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid);
/* Find a BTS context based on BVCI+NSEI tuple */
@@ -160,7 +163,7 @@ int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
uint16_t cid);
/* Wrapper around TLV parser to parse BSSGP IEs */
-static inline int bssgp_tlv_parse(struct tlv_parsed *tp, uint8_t *buf, int len)
+static inline int bssgp_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len)
{
return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0);
}
diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h
index fddc896a..45753292 100644
--- a/include/osmocom/gprs/gprs_ns2.h
+++ b/include/osmocom/gprs/gprs_ns2.h
@@ -7,9 +7,12 @@
#include <netinet/in.h>
#include <osmocom/core/prim.h>
+#include <osmocom/gprs/protocol/gsm_08_16.h>
+#include <osmocom/gprs/frame_relay.h>
struct osmo_sockaddr;
struct osmo_sockaddr_str;
+struct osmo_fr_network;
struct gprs_ns2_inst;
struct gprs_ns2_nse;
@@ -29,6 +32,13 @@ enum gprs_ns2_vc_mode {
NS2_VC_MODE_ALIVE,
};
+/*! Osmocom NS link layer types */
+enum gprs_ns2_ll {
+ GPRS_NS2_LL_UDP, /*!< NS/UDP/IP */
+ GPRS_NS2_LL_FR, /*!< NS/FR */
+ GPRS_NS2_LL_FR_GRE, /*!< NS/FR/GRE/IP */
+};
+
/*! Osmocom NS primitives according to 48.016 5.2 Service primitves */
enum gprs_ns2_prim {
PRIM_NS_UNIT_DATA,
@@ -36,6 +46,17 @@ enum gprs_ns2_prim {
PRIM_NS_STATUS,
};
+extern const struct value_string gprs_ns2_prim_strs[];
+extern const struct value_string gprs_ns2_lltype_strs[];
+
+/*! Obtain a human-readable string for NS primitives */
+static inline const char *gprs_ns2_prim_str(enum gprs_ns2_prim val)
+{ return get_value_string(gprs_ns2_prim_strs, val); }
+
+/*! Obtain a human-readable string for NS link-layer type */
+static inline const char *gprs_ns2_lltype_str(enum gprs_ns2_ll val)
+{ return get_value_string(gprs_ns2_lltype_strs, val); }
+
/*! Osmocom NS primitives according to 48.016 5.2.2.4 Service primitves */
enum gprs_ns2_congestion_cause {
NS_CONG_CAUSE_BACKWARD_BEGIN,
@@ -55,6 +76,12 @@ enum gprs_ns2_affecting_cause {
NS_AFF_CAUSE_SNS_FAILURE,
};
+extern const struct value_string gprs_ns2_aff_cause_prim_strs[];
+
+/*! Obtain a human-readable string for NS affective cause in primitives */
+static inline const char *gprs_ns2_aff_cause_prim_str(enum gprs_ns2_affecting_cause val)
+{ return get_value_string(gprs_ns2_aff_cause_prim_strs, val); }
+
/*! Osmocom NS primitives according to 48.016 5.2.2.7 Service primitves */
enum gprs_ns2_change_ip_endpoint {
NS_ENDPOINT_NO_CHANGE,
@@ -62,6 +89,12 @@ enum gprs_ns2_change_ip_endpoint {
NS_ENDPOINT_CONFIRM_CHANGE,
};
+extern const struct value_string gprs_ns2_cause_strs[];
+
+/*! Obtain a human-readable string for NS primitives */
+static inline const char *gprs_ns2_cause_str(enum ns_cause val)
+{ return get_value_string(gprs_ns2_cause_strs, val); }
+
struct osmo_gprs_ns2_prim {
struct osmo_prim_hdr oph;
@@ -71,6 +104,7 @@ struct osmo_gprs_ns2_prim {
union {
struct {
enum gprs_ns2_change_ip_endpoint change;
+ uint32_t link_selector;
/* TODO: implement resource distribution
* add place holder for the link selector */
long long _resource_distribution_placeholder1;
@@ -82,6 +116,7 @@ struct osmo_gprs_ns2_prim {
} congestion;
struct {
enum gprs_ns2_affecting_cause cause;
+ char *nsvc;
/* 48.016 5.2.2.6 transfer capability */
int transfer;
/* osmocom specific */
@@ -108,7 +143,8 @@ typedef int (*gprs_ns2_foreach_nsvc_cb)(struct gprs_ns2_vc *nsvc, void *ctx);
int gprs_ns2_nse_foreach_nsvc(struct gprs_ns2_nse *nse,
gprs_ns2_foreach_nsvc_cb cb, void *cb_data);
struct gprs_ns2_nse *gprs_ns2_nse_by_nsei(struct gprs_ns2_inst *nsi, uint16_t nsei);
-struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nsei);
+struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nsei,
+ enum gprs_ns2_ll linklayer);
uint16_t gprs_ns2_nse_nsei(struct gprs_ns2_nse *nse);
void gprs_ns2_free_nse(struct gprs_ns2_nse *nse);
void gprs_ns2_free_nses(struct gprs_ns2_inst *nsi);
@@ -126,6 +162,23 @@ struct gprs_ns2_vc_bind *gprs_ns2_ip_bind_by_sockaddr(struct gprs_ns2_inst *nsi,
const struct osmo_sockaddr *sockaddr);
void gprs_ns2_bind_set_mode(struct gprs_ns2_vc_bind *bind, enum gprs_ns2_vc_mode mode);
+/* FR VL driver */
+struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
+ struct gprs_ns2_inst *nsi,
+ const char *netif);
+const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind);
+int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
+ const char *netif,
+ struct osmo_fr_network *fr_network,
+ enum osmo_fr_role fr_role,
+ struct gprs_ns2_vc_bind **result);
+int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind);
+struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind, uint16_t dlci);
+struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
+ uint16_t nsei,
+ uint16_t nsvci,
+ uint16_t dlci);
+
/* create a VC connection */
struct gprs_ns2_vc *gprs_ns2_ip_connect(struct gprs_ns2_vc_bind *bind,
const struct osmo_sockaddr *remote,
@@ -168,15 +221,18 @@ int gprs_ns2_frgre_bind(struct gprs_ns2_inst *nsi,
int dscp,
struct gprs_ns2_vc_bind **result);
int gprs_ns2_is_frgre_bind(struct gprs_ns2_vc_bind *bind);
+uint16_t gprs_ns2_fr_nsvc_dlci(struct gprs_ns2_vc *nsvc);
struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_nse(
struct gprs_ns2_nse *nse,
const struct osmo_sockaddr *sockaddr);
void gprs_ns2_start_alive_all_nsvcs(struct gprs_ns2_nse *nse);
-const char *gprs_ns2_cause_str(int cause);
+
+/* VC information */
const char *gprs_ns2_ll_str(struct gprs_ns2_vc *nsvc);
char *gprs_ns2_ll_str_buf(char *buf, size_t buf_len, struct gprs_ns2_vc *nsvc);
char *gprs_ns2_ll_str_c(const void *ctx, struct gprs_ns2_vc *nsvc);
+const char *gprs_ns2_nsvc_state_name(struct gprs_ns2_vc *nsvc);
/* vty */
int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi,
diff --git a/include/osmocom/gprs/protocol/gsm_08_18.h b/include/osmocom/gprs/protocol/gsm_08_18.h
index af6caf32..4c04f6b9 100644
--- a/include/osmocom/gprs/protocol/gsm_08_18.h
+++ b/include/osmocom/gprs/protocol/gsm_08_18.h
@@ -1,4 +1,5 @@
/*! \file gsm_08_18.h */
+/* Updated to reflect TS 48.018 version 15.0.0 Release 15 */
#pragma once
@@ -8,17 +9,23 @@
#define BVCI_SIGNALLING 0x0000
#define BVCI_PTM 0x0001
+/* typo backwards compatiblity */
+#define BSSGP_PDUT_RA_CAPA_UDPATE BSSGP_PDUT_RA_CAPA_UPDATE
+
/*! BSSGP PDU types (Section 11.3.26 / Table 11.27) */
enum bssgp_pdu_type {
/* PDUs between RL and BSSGP SAPs */
BSSGP_PDUT_DL_UNITDATA = 0x00,
BSSGP_PDUT_UL_UNITDATA = 0x01,
BSSGP_PDUT_RA_CAPABILITY = 0x02,
- BSSGP_PDUT_PTM_UNITDATA = 0x03,
+ /* PDUs between MBMS SAPs */
+ BSSGP_PDUT_PTM_UNITDATA = 0x03, /* reserved in later specs */
+ BSSGP_PDUT_DL_MMBS_UNITDATA = 0x04,
+ BSSGP_PDUT_UL_MMBS_UNITDATA = 0x05,
/* PDUs between GMM SAPs */
BSSGP_PDUT_PAGING_PS = 0x06,
BSSGP_PDUT_PAGING_CS = 0x07,
- BSSGP_PDUT_RA_CAPA_UDPATE = 0x08,
+ BSSGP_PDUT_RA_CAPA_UPDATE = 0x08,
BSSGP_PDUT_RA_CAPA_UPDATE_ACK = 0x09,
BSSGP_PDUT_RADIO_STATUS = 0x0a,
BSSGP_PDUT_SUSPEND = 0x0b,
@@ -27,6 +34,11 @@ enum bssgp_pdu_type {
BSSGP_PDUT_RESUME = 0x0e,
BSSGP_PDUT_RESUME_ACK = 0x0f,
BSSGP_PDUT_RESUME_NACK = 0x10,
+ BSSGP_PDUT_PAGING_PS_REJECT = 0x11,
+ BSSGP_PDUT_DUMMY_PAGING_PS = 0x12,
+ BSSGP_PDUT_DUMMY_PAGING_PS_RESP = 0x13,
+ BSSGP_PDUT_MS_REGISTR_ENQ = 0x14,
+ BSSGP_PDUT_MS_REGISTR_ENQ_RESP = 0x15,
/* PDus between NM SAPs */
BSSGP_PDUT_BVC_BLOCK = 0x20,
BSSGP_PDUT_BVC_BLOCK_ACK = 0x21,
@@ -41,8 +53,11 @@ enum bssgp_pdu_type {
BSSGP_PDUT_FLUSH_LL = 0x2a,
BSSGP_PDUT_FLUSH_LL_ACK = 0x2b,
BSSGP_PDUT_LLC_DISCARD = 0x2c,
+ BSSGP_PDUT_FLOW_CONTROL_PFC = 0x2d,
+ BSSGP_PDUT_FLOW_CONTROL_PFC_ACK = 0x2e,
BSSGP_PDUT_SGSN_INVOKE_TRACE = 0x40,
BSSGP_PDUT_STATUS = 0x41,
+ BSSGP_PDUT_OVERLOAD = 0x42,
/* PDUs between PFM SAP's */
BSSGP_PDUT_DOWNLOAD_BSS_PFC = 0x50,
BSSGP_PDUT_CREATE_BSS_PFC = 0x51,
@@ -52,6 +67,16 @@ enum bssgp_pdu_type {
BSSGP_PDUT_MODIFY_BSS_PFC_ACK = 0x55,
BSSGP_PDUT_DELETE_BSS_PFC = 0x56,
BSSGP_PDUT_DELETE_BSS_PFC_ACK = 0x57,
+ BSSGP_PDUT_DELETE_BSS_PFC_REQ = 0x58,
+ BSSGP_PDUT_PS_HO_REQUIRED = 0x59,
+ BSSGP_PDUT_PS_HO_REQUIRED_ACK = 0x5a,
+ BSSGP_PDUT_PS_HO_REQUIRED_NACK = 0x5b,
+ BSSGP_PDUT_PS_HO_REQUEST = 0x5c,
+ BSSGP_PDUT_PS_HO_REQUEST_ACK = 0x5d,
+ BSSGP_PDUT_PS_HO_REQUEST_NACK = 0x5e,
+ BSSGP_PDUT_PS_HO_COMPLETE = 0x91,
+ BSSGP_PDUT_PS_HO_CANCEL = 0x92,
+ BSSGP_PDUT_PS_HO_COMPLETE_ACK = 0x93,
};
/*! BSSGP User-Data header (Section 10.2.1 and 10.2.2) */
@@ -68,7 +93,7 @@ struct bssgp_normal_hdr {
uint8_t data[0]; /*!< optional/conditional IEs as TLVs */
};
-/*! BSSGP Information Element Identifiers */
+/*! BSSGP Information Element Identifiers (Section 11.3 / Table 11.3) */
enum bssgp_iei_type {
BSSGP_IE_ALIGNMENT = 0x00,
BSSGP_IE_BMAX_DEFAULT_MS = 0x01,
@@ -116,6 +141,105 @@ enum bssgp_iei_type {
BSSGP_IE_FEATURE_BITMAP = 0x3b,
BSSGP_IE_BUCKET_FULL_RATIO = 0x3c,
BSSGP_IE_SERVICE_UTRAN_CCO = 0x3d,
+ BSSGP_IE_NSEI = 0x3e,
+ BSSGP_IE_RRLP_APDU = 0x3f,
+ BSSGP_IE_LCS_QOS = 0x40,
+ BSSGP_IE_LCS_CLIENT_TYPE = 0x41,
+ BSSGP_IE_REQUESTED_GPS_AST_DATA = 0x42,
+ BSSGP_IE_LOCATION_TYPE = 0x43,
+ BSSGP_IE_LOCATION_ESTIMATE = 0x44,
+ BSSGP_IE_POSITIONING_DATA = 0x45,
+ BSSGP_IE_DECIPHERING_KEYS = 0x46,
+ BSSGP_IE_LCS_PRIORITY = 0x47,
+ BSSGP_IE_LCS_CAUSE = 0x48,
+ BSSGP_IE_LCS_CAPABILITY = 0x49,
+ BSSGP_IE_RRLP_FLAGS = 0x4a,
+ BSSGP_IE_RIM_APP_IDENTITY = 0x4b,
+ BSSGP_IE_RIM_SEQ_NR = 0x4c,
+ BSSGP_IE_RIM_REQ_APP_CONTAINER = 0x4d,
+ BSSGP_IE_RAN_INFO_APP_CONTAINER = 0x4e,
+ BSSGP_IE_RIM_PDU_INDICATIONS = 0x4f,
+ BSSGP_IE_PFC_FLOW_CTRL_PARAMS = 0x52,
+ BSSGP_IE_GLOBAL_CN_ID = 0x53,
+ BSSGP_IE_RIM_ROUTING_INFO = 0x54,
+ BSSGP_IE_RIM_PROTOCOL_VERSION = 0x55,
+ BSSGP_IE_APP_ERROR_CONTAINER = 0x56,
+ BSSGP_IE_RI_REQ_RIM_CONTAINER = 0x57,
+ BSSGP_IE_RI_RIM_CONTAINER = 0x58,
+ BSSGP_IE_RI_APP_ERROR_RIM_CONT = 0x59,
+ BSSGP_IE_RI_ACK_RIM_CONTAINER = 0x5a,
+ BSSGP_IE_RI_ERROR_RIM_COINTAINER= 0x5b,
+ BSSGP_IE_TMGI = 0x5c,
+ BSSGP_IE_MBMS_SESSION_ID = 0x5d,
+ BSSGP_IE_MBMS_SESSION_DURATION = 0x5e,
+ BSSGP_IE_MBMS_SA_ID_LIST = 0x5f,
+ BSSGP_IE_MBMS_RESPONSE = 0x60,
+ BSSGP_IE_MBMS_RA_LIST = 0x61,
+ BSSGP_IE_MBMS_SESSION_INFO = 0x62,
+ BSSGP_IE_MBMS_STOP_CAUSE = 0x63,
+ BSSGP_IE_SBSS_TO_TBSS_TR_CONT = 0x64,
+ BSSGP_IE_TBSS_TO_SBSS_TR_CONT = 0x65,
+ BSSGP_IE_NAS_CONT_FOR_PS_HO = 0x66,
+ BSSGP_IE_PFC_TO_BE_SETUP_LIST = 0x67,
+ BSSGP_IE_LIST_OF_SETUP_PFC = 0x68,
+ BSSGP_IE_EXT_FEATURE_BITMAP = 0x69,
+ BSSGP_IE_SRC_TO_TGT_TR_CONT = 0x6a,
+ BSSGP_IE_TGT_TO_SRC_TR_CONT = 0x6b,
+ BSSGP_IE_NC_ID = 0x6c,
+ BSSGP_IE_PAGE_MODE = 0x6d,
+ BSSGP_IE_CONTAINER_ID = 0x6e,
+ BSSGP_IE_GLOBAL_TFI = 0x6f,
+ BSSGP_IE_IMEI = 0x70,
+ BSSGP_IE_TIME_TO_MBMS_DATA_XFR = 0x71,
+ BSSGP_IE_MBMS_SESSION_REP_NR = 0x72,
+ BSSGP_IE_INTER_RAT_HO_INFO = 0x73,
+ BSSGP_IE_PS_HO_COMMAND = 0x74,
+ BSSGP_IE_PS_HO_INDICATIONS = 0x75,
+ BSSGP_IE_SI_PSI_CONTAINER = 0x76,
+ BSSGP_IE_ACTIVE_PFC_LIST = 0x77,
+ BSSGP_IE_VELOCITY_DATA = 0x78,
+ BSSGP_IE_DTM_HO_COMMAND = 0x79,
+ BSSGP_IE_CS_INDICATION = 0x7a,
+ BSSGP_IE_RQD_GANNS_AST_DATA = 0x7b,
+ BSSGP_IE_GANSS_LOCATION_TYPE = 0x7c,
+ BSSGP_IE_GANSS_POSITIONING_DATA = 0x7d,
+ BSSGP_IE_FLOW_CTRL_GRANULARITY = 0x7e,
+ BSSGP_IE_ENB_ID = 0x7f,
+ BSSGP_IE_EUTRAN_IRAT_HO_INFO = 0x80,
+ BSSGP_IE_SUB_PID4RAT_FREQ_PRIO = 0x81,
+ BSSGP_IE_REQ4IRAT_HO_INFO = 0x82,
+ BSSGP_IE_RELIABLE_IRAT_HO_INFO = 0x83,
+ BSSGP_IE_SON_TRANSFER_APP_ID = 0x84,
+ BSSGP_IE_CSG_ID = 0x85,
+ BSSGP_IE_TAC = 0x86,
+ BSSGP_IE_REDIRECT_ATTEMPT_FLAG = 0x87,
+ BSSGP_IE_REDIRECTION_INDICATION = 0x88,
+ BSSGP_IE_REDIRECTION_COMPLETED = 0x89,
+ BSSGP_IE_UNCONF_SEND_STATE_VAR = 0x8a,
+ BSSGP_IE_IRAT_MEASUREMENT_CONF = 0x8b,
+ BSSGP_IE_SCI = 0x8c,
+ BSSGP_IE_GGSN_PGW_LOCATION = 0x8d,
+ BSSGP_IE_SELECTED_PLMN_ID = 0x8e,
+ BSSGP_IE_PRIO_CLASS_IND = 0x8f,
+ BSSGP_IE_SOURCE_CELL_ID = 0x90,
+ BSSGP_IE_IRAT_MEAS_CFG_E_EARFCN = 0x91,
+ BSSGP_IE_EDRX_PARAMETERS = 0x92,
+ BSSGP_IE_T_UNTIL_NEXT_PAGING = 0x93,
+ BSSGP_IE_COVERAGE_CLASS = 0x98,
+ BSSGP_IE_PAGING_ATTEMPT_INFO = 0x99,
+ BSSGP_IE_EXCEPTION_REPORT_FLAG = 0x9a,
+ BSSGP_IE_OLD_RA_ID = 0x9b,
+ BSSGP_IE_ATTACH_IND = 0x9c,
+ BSSGP_IE_PLMN_ID = 0x9d,
+ BSSGP_IE_MME_QUERY = 0x9e,
+ BSSGP_IE_SGSN_GROUP_ID = 0x9f,
+ BSSGP_IE_ADDITIONAL_PTMSI = 0xa0,
+ BSSGP_IE_UE_USAGE_TYPE = 0xa1,
+ BSSGP_IE_MLAT_TIMER = 0xa2,
+ BSSGP_IE_MLAT_TA = 0xa3,
+ BSSGP_IE_MS_SYNC_ACCURACY = 0xa4,
+ BSSGP_IE_BTS_RX_ACCURACY_LVL = 0xa5,
+ BSSGP_IE_TA_REQ = 0xa6,
};
/*! Cause coding (Section 11.3.8 / Table 11.10) */
diff --git a/include/osmocom/gsm/bts_features.h b/include/osmocom/gsm/bts_features.h
index 341ba405..98e6c7bb 100644
--- a/include/osmocom/gsm/bts_features.h
+++ b/include/osmocom/gsm/bts_features.h
@@ -26,6 +26,7 @@ enum osmo_bts_features {
BTS_FEAT_ETWS_PN,
BTS_FEAT_PAGING_COORDINATION, /* BTS hands CS paging to PCU/PACCH */
BTS_FEAT_IPV6_NSVC,
+ BTS_FEAT_ACCH_REP,
_NUM_BTS_FEAT
};
diff --git a/include/osmocom/gsm/gsm48_ie.h b/include/osmocom/gsm/gsm48_ie.h
index 339aa136..b79cbfcb 100644
--- a/include/osmocom/gsm/gsm48_ie.h
+++ b/include/osmocom/gsm/gsm48_ie.h
@@ -119,3 +119,7 @@ struct gsm_sysinfo_freq {
/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
uint8_t len, uint8_t mask, uint8_t frqt);
+
+/* decode "CSN.1 encoded Classmark 3" (10.5.1.7) */
+int gsm48_decode_classmark3(struct gsm48_classmark3 *classmark3_out,
+ const uint8_t *classmark3, size_t classmark3_len);
diff --git a/include/osmocom/gsm/protocol/gsm_04_08.h b/include/osmocom/gsm/protocol/gsm_04_08.h
index f8f2eabd..a103c32b 100644
--- a/include/osmocom/gsm/protocol/gsm_04_08.h
+++ b/include/osmocom/gsm/protocol/gsm_04_08.h
@@ -58,6 +58,175 @@ struct gsm48_classmark2 {
#endif
} __attribute__ ((packed));
+/* Chapter 10.5.1.7 */
+struct gsm48_classmark3 {
+ uint8_t a5_bits;
+ uint8_t mult_band_supp;
+ uint8_t assoc_radio_cap_1;
+ uint8_t assoc_radio_cap_2;
+
+ struct {
+ bool present;
+ uint8_t r_gsm_assoc_radio_cap;
+ } r_support;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } hscsd_mult_slot_cap;
+
+ bool ucs2_treatment;
+ bool extended_meas_cap;
+
+ struct {
+ bool present;
+ uint8_t sms_value;
+ uint8_t sm_value;
+ } ms_meas_cap;
+
+ struct {
+ bool present;
+ uint8_t method;
+ } ms_pos_method_cap;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } ecsd_multislot_cap;
+
+ struct {
+ bool present;
+ bool mod_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } rf_pwr_cap_1;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } rf_pwr_cap_2;
+
+ } psk8_struct;
+
+ struct {
+ bool present;
+ uint8_t value;
+ uint8_t assoc_radio_cap;
+ } gsm_400_bands_supp;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } gsm_850_assoc_radio_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } gsm_1900_assoc_radio_cap;
+
+ bool umts_fdd_rat_cap;
+ bool umts_tdd_rat_cap;
+ bool cdma200_rat_cap;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ bool single_slot_dtm;
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } dtm_egprs_multislot_cap;
+ } dtm_gprs_multislot_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } single_band_supp;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } gsm_750_assoc_radio_cap;
+
+ bool umts_1_28_mcps_tdd_rat_cap;
+ bool geran_feature_package;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } extended_dtm_egprs_multislot_cap;
+ } extended_dtm_gprs_multislot_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } high_multislot_cap;
+
+ bool geran_feature_package_2;
+ uint8_t gmsk_multislot_power_prof;
+ uint8_t psk8_multislot_power_prof;
+
+ struct {
+ bool present;
+ uint8_t value;
+ uint8_t assoc_radio_cap;
+ } t_gsm_400_bands_supp;
+
+ uint8_t dl_advanced_rx_perf;
+ bool dtm_enhancements_cap;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ bool offset_required;
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } dtm_egprs_high_multislot_cap;
+ } dtm_gprs_high_multislot_cap;
+
+ bool repeated_acch_capability;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } gsm_710_assoc_radio_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } t_gsm_810_assoc_radio_cap;
+
+ bool ciphering_mode_setting_cap;
+ bool add_pos_cap;
+ bool e_utra_fdd_supp;
+ bool e_utra_tdd_supp;
+ bool e_utra_meas_rep_supp;
+ bool prio_resel_supp;
+ bool utra_csg_cells_rep;
+
+ uint8_t vamos_level;
+ uint8_t tighter_capability;
+
+ bool sel_ciph_dl_sacch;
+
+ uint8_t cs_ps_srvcc_geran_utra;
+ uint8_t cs_ps_srvcc_geran_eutra;
+
+ bool geran_net_sharing;
+ bool e_utra_wb_rsrq_meas_supp;
+ bool er_band_support;
+ bool utra_mult_band_ind_supp;
+ bool e_utra_mult_band_ind_supp;
+ bool extended_tsc_set_cap_supp;
+ bool extended_earfcn_val_range;
+};
+
struct osmo_gsm48_classmark {
bool classmark1_set;
struct gsm48_classmark1 classmark1;
@@ -1638,9 +1807,12 @@ enum gsm48_rr_cause {
GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03,
GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04,
GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05,
+ GSM48_RR_CAUSE_UTRAN_CFG_UNK = 0x06,
GSM48_RR_CAUSE_HNDOVER_IMP = 0x08,
GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x09,
GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x0a,
+ GSM48_RR_CAUSE_LEAVE_GROUP_CA = 0x0b,
+ GSM48_RR_CAUSE_LOW_LEVEL_FAIL = 0x0c,
GSM48_RR_CAUSE_CALL_CLEARED = 0x41,
GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f,
GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
diff --git a/include/osmocom/gsm/protocol/gsm_04_08_gprs.h b/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
index 86c5b016..dbac2597 100644
--- a/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
+++ b/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
@@ -98,6 +98,7 @@ enum gsm48_gprs_ie_mm {
GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */
GSM48_IE_GMM_PS_LCS_CAPA = 0x33, /* 10.5.5.22 */
GSM48_IE_GMM_GMM_MBMS_CTX_ST = 0x35, /* 10.5.7.6 */
+ GSM48_IE_GMM_NET_FEAT_SUPPORT = 0xB0, /* 10.5.5.23 */
};
enum gsm48_gprs_ie_sm {
diff --git a/include/osmocom/gsm/protocol/gsm_08_58.h b/include/osmocom/gsm/protocol/gsm_08_58.h
index cd13a7dc..1ed4438c 100644
--- a/include/osmocom/gsm/protocol/gsm_08_58.h
+++ b/include/osmocom/gsm/protocol/gsm_08_58.h
@@ -116,6 +116,24 @@ struct abis_rsl_cchan_hdr {
uint8_t data[0]; /*!< message payload data */
} __attribute__ ((packed));
+/* Osmocom specific IE to negotiate repeated ACCH capabilities */
+struct abis_rsl_osmo_rep_acch_cap {
+#if OSMO_IS_BIG_ENDIAN
+ uint8_t reserved:1,
+ rxqual:3,
+ ul_sacch:1,
+ dl_sacch:1,
+ dl_facch_all:1,
+ dl_facch_cmd:1;
+#elif OSMO_IS_LITTLE_ENDIAN
+ uint8_t dl_facch_cmd:1,
+ dl_facch_all:1,
+ dl_sacch:1,
+ ul_sacch:1,
+ rxqual:3,
+ reserved:1;
+#endif
+} __attribute__ ((packed));
/* Chapter 9.1 */
/* RSL Message Discriminator: RLL */
@@ -337,6 +355,9 @@ enum abis_rsl_ie {
RSL_IE_SIEMENS_HIGHEST_RATE = 0x4e,
RSL_IE_SIEMENS_SUGGESTED_RATE = 0x4f,
+ /* Osmocom specific */
+ RSL_IE_OSMO_REP_ACCH_CAP= 0x60,
+
/* ip.access */
RSL_IE_IPAC_SRTP_CONFIG = 0xe0,
RSL_IE_IPAC_PROXY_UDP = 0xe1,
diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h
index eb7ee35e..add57e22 100644
--- a/include/osmocom/vty/command.h
+++ b/include/osmocom/vty/command.h
@@ -67,9 +67,6 @@ struct host {
/*! VTY application information */
const struct vty_app_info *app_info;
-
- /*! Whether the expert mode is enabled. */
- bool expert_mode;
};
/*! There are some command levels which called from command node. */
@@ -467,6 +464,8 @@ enum vty_ref_gen_mode {
VTY_REF_GEN_MODE_DEFAULT = 0,
/*! Expert mode: all commands including hidden, excluding deprecated. */
VTY_REF_GEN_MODE_EXPERT,
+ /*! "Inverse" mode: only hidden commands. */
+ VTY_REF_GEN_MODE_HIDDEN,
};
extern const struct value_string vty_ref_gen_mode_names[];
diff --git a/include/osmocom/vty/vty.h b/include/osmocom/vty/vty.h
index 6a82d7e3..d34433fa 100644
--- a/include/osmocom/vty/vty.h
+++ b/include/osmocom/vty/vty.h
@@ -165,6 +165,9 @@ struct vty {
/*! When reading from a config file, these are the indenting characters expected for children of
* the current VTY node. */
char *indent;
+
+ /*! Whether the expert mode is enabled. */
+ bool expert_mode;
};
/* Small macro to determine newline is newline only or linefeed needed. */
diff --git a/src/Makefile.am b/src/Makefile.am
index b2c9204f..5ff1a420 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,7 +4,7 @@
LIBVERSION=16:0:0
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS) $(LIBMNL_CFLAGS)
if ENABLE_PSEUDOTALLOC
AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
@@ -76,5 +76,10 @@ libosmocore_la_SOURCES += logging_systemd.c
libosmocore_la_LIBADD += $(SYSTEMD_LIBS)
endif
+if ENABLE_LIBMNL
+libosmocore_la_SOURCES += mnl.c
+libosmocore_la_LIBADD += $(LIBMNL_LIBS)
+endif
+
crc%gen.c: crcXXgen.c.tpl
$(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
diff --git a/src/bitvec.c b/src/bitvec.c
index d7f32fbd..2b4e8c98 100644
--- a/src/bitvec.c
+++ b/src/bitvec.c
@@ -272,7 +272,7 @@ int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits)
if (bit < 0)
return bit;
if (bit)
- ui |= (1 << (num_bits - i - 1));
+ ui |= ((unsigned)1 << (num_bits - i - 1));
bv->cur_bit++;
}
diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am
index 65c35529..b1407275 100644
--- a/src/gb/Makefile.am
+++ b/src/gb/Makefile.am
@@ -4,7 +4,10 @@
LIBVERSION=11:0:0
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing $(TALLOC_CFLAGS)
+AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing \
+ $(TALLOC_CFLAGS) \
+ $(LIBMNL_CFLAGS) \
+ $(NULL)
# FIXME: this should eventually go into a milenage/Makefile.am
noinst_HEADERS = common_vty.h gb_internal.h gprs_bssgp_internal.h gprs_ns2_internal.h
@@ -21,9 +24,9 @@ libosmogb_la_LIBADD = $(TALLOC_LIBS) \
libosmogb_la_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c gprs_ns_sns.c \
gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c \
gprs_bssgp_bss.c \
- gprs_ns2.c gprs_ns2_udp.c gprs_ns2_frgre.c gprs_ns2_vc_fsm.c gprs_ns2_sns.c \
+ gprs_ns2.c gprs_ns2_udp.c gprs_ns2_frgre.c gprs_ns2_fr.c gprs_ns2_vc_fsm.c gprs_ns2_sns.c \
gprs_ns2_message.c gprs_ns2_vty.c \
- common_vty.c
+ common_vty.c frame_relay.c
endif
EXTRA_DIST = libosmogb.map
diff --git a/src/gb/common_vty.c b/src/gb/common_vty.c
index eb665d52..42f3404a 100644
--- a/src/gb/common_vty.c
+++ b/src/gb/common_vty.c
@@ -40,15 +40,21 @@
int gprs_log_filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
+ const void *nse = ctx->ctx[LOG_CTX_GB_NSE];
const void *nsvc = ctx->ctx[LOG_CTX_GB_NSVC];
const void *bvc = ctx->ctx[LOG_CTX_GB_BVC];
+ /* Filter on the NS Entity */
+ if ((tar->filter_map & (1 << LOG_FLT_GB_NSE)) != 0
+ && nse && (nse == tar->filter_data[LOG_FLT_GB_NSE]))
+ return 1;
+
/* Filter on the NS Virtual Connection */
if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0
&& nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC]))
return 1;
- /* Filter on the NS Virtual Connection */
+ /* Filter on the BSSGP Virtual Connection */
if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0
&& bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
return 1;
diff --git a/src/gb/frame_relay.c b/src/gb/frame_relay.c
new file mode 100644
index 00000000..ae6bbf58
--- /dev/null
+++ b/src/gb/frame_relay.c
@@ -0,0 +1,966 @@
+/*! \file frame_relay.c
+ * Implement frame relay/PVC by Q.933
+ */
+/* (C) 2020 Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <osmocom/gprs/frame_relay.h>
+#include <osmocom/core/endian.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/tlv.h>
+
+#define LOGPFRL(frl, lvl, fmt, args ...) \
+ LOGP(DFR, lvl, "%s: " fmt, (frl)->name, ## args)
+
+#define DFR DLNS
+
+/* Table 4-2/Q.931 */
+enum q931_msgtype {
+ /* Call establishment message */
+ Q931_MSGT_ALERTING = 0x01,
+ Q931_MSGT_CALL_PROCEEDING = 0x02,
+ Q931_MSGT_CONNECT = 0x07,
+ Q931_MSGT_CONNECT_ACK = 0x0f,
+ Q931_MSGT_PROGRESS = 0x03,
+ Q931_MSGT_SETUP = 0x05,
+ Q931_MSGT_SETUP_ACK = 0x0d,
+ /* Call information phase message */
+ Q931_MSGT_RESUME = 0x26,
+ Q931_MSGT_RESUME_ACK = 0x2e,
+ Q931_MSGT_RESUME_REJ = 0x22,
+ Q931_MSGT_SUSPEND = 0x25,
+ Q931_MSGT_SUSPEND_ACK = 0x2d,
+ Q931_MSGT_USER_INFO = 0x20,
+ /* Call clearing message */
+ Q931_MSGT_DISCONNECT = 0x45,
+ Q931_MSGT_RELEASE = 0x4d,
+ Q931_MSGT_RELEASE_COMPLETE = 0x5a,
+ Q931_MSGT_RESTART = 0x46,
+ Q931_MSGT_RESTART_ACK = 0x4e,
+ /* Miscellaneous messages */
+ Q931_MSGT_SEGMENT = 0x60,
+ Q931_MSGT_CONGESTION_CONTROL = 0x79,
+ Q931_MSGT_IFORMATION = 0x7b,
+ Q931_MSGT_NOTIFY = 0x6e,
+ Q931_MSGT_STATUS = 0x7d,
+ Q931_MSGT_STATUS_ENQUIRY = 0x75,
+};
+
+
+/* Figure A.1/Q.933 Report type information element */
+enum q933_type_of_report {
+ Q933_REPT_FULL_STATUS = 0x00,
+ Q933_REPT_LINK_INTEGRITY_VERIF = 0x01,
+ Q933_REPT_SINGLE_PVC_ASYNC_STS = 0x02,
+};
+
+/* Q.933 Section A.3 */
+enum q933_iei {
+ Q933_IEI_REPORT_TYPE = 0x51,
+ Q933_IEI_LINK_INT_VERIF = 0x53,
+ Q933_IEI_PVC_STATUS = 0x57,
+};
+
+/* Q.933 Section A.3.3 */
+enum q933_pvc_status {
+ Q933_PVC_STATUS_DLC_ACTIVE = 0x02,
+ Q933_PVC_STATUS_DLC_DELETE = 0x04,
+ Q933_PVC_STATUS_DLC_NEW = 0x08,
+};
+
+
+
+#define LAPF_UI 0x03 /* UI control word */
+#define Q931_PDISC_CC 0x08 /* protocol discriminator */
+#define LMI_Q933A_CALLREF 0x00 /* NULL call-ref */
+
+/* LMI DLCI values */
+#define LMI_Q933A_DLCI 0 /* Q.933A DLCI */
+#define LMI_CISCO_DLCI 1023 /* Cisco DLCI */
+
+/* maximum of supported */
+#define MAX_SUPPORTED_PVC 10
+
+/* TODO: add counters since good connection */
+
+/* Message header of the L3 payload of a Q.933 Annex A message */
+struct q933_a_hdr {
+ uint8_t prot_disc;
+ uint8_t call_ref;
+ uint8_t msg_type;
+} __attribute__((packed));
+
+/* Value part of the Q.933 Annex A.3.3 IE */
+struct q933_a_pvc_sts {
+ uint8_t dlci_msb:6,
+ spare:1,
+ ext0:1;
+ uint8_t space1:3,
+ dlci_lsb:4,
+ ext1:1;
+ uint8_t reserved:1,
+ active:1,
+ delete:1,
+ new:1,
+ spare2:3,
+ ext2:1;
+
+} __attribute__((packed));
+
+/* RX Message: 14 [ 00 01 03 08 00 75 95 01 01 00 03 02 01 00 ] */
+/* RX Message: 13 [ 00 01 03 08 00 75 51 01 00 53 02 01 00 ] */
+
+const struct value_string osmo_fr_role_names[] = {
+ { FR_ROLE_USER_EQUIPMENT, "USER" },
+ { FR_ROLE_NETWORK_EQUIPMENT, "NETWORK" },
+ { 0, NULL }
+};
+
+/* Table A.4/Q.933 */
+struct osmo_tdef fr_tdefs[] = {
+ {
+ .T=391,
+ .default_val = 10,
+ .min_val = 5,
+ .max_val = 30,
+ .desc = "Link integrity verification polling timer",
+ .unit = OSMO_TDEF_S,
+ }, {
+ .T=392,
+ .default_val = 15,
+ .min_val = 5,
+ .max_val = 30,
+ .desc = "Polling verification timer",
+ .unit = OSMO_TDEF_S,
+ },
+ {}
+};
+
+static const struct tlv_definition q933_att_tlvdef = {
+ .def = {
+ [Q933_IEI_REPORT_TYPE] = { TLV_TYPE_TLV },
+ [Q933_IEI_LINK_INT_VERIF] = { TLV_TYPE_TLV },
+ [Q933_IEI_PVC_STATUS] = { TLV_TYPE_TLV },
+ },
+};
+
+static void check_link_state(struct osmo_fr_link *link, bool valid);
+
+static inline uint16_t q922_to_dlci(const uint8_t *hdr)
+{
+ return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
+}
+
+
+static inline void dlci_to_q922(uint8_t *hdr, uint16_t dlci)
+{
+ hdr[0] = (dlci >> 2) & 0xFC;
+ hdr[1] = ((dlci << 4) & 0xF0) | 0x01;
+}
+
+/* allocate a message buffer and put Q.933 Annex A headers (L2 + L3) */
+static struct msgb *q933_msgb_alloc(uint16_t dlci, uint8_t prot_disc, uint8_t msg_type)
+{
+ struct msgb *msg = msgb_alloc_headroom(1600+64, 64, "FR Q.933 Tx");
+ struct q933_a_hdr *qh;
+
+ if (!msg)
+ return NULL;
+
+ msg->l1h = msgb_put(msg, 2);
+ dlci_to_q922(msg->l1h, dlci);
+
+ /* LAPF UI control */
+ msg->l2h = msgb_put(msg, 1);
+ *msg->l2h = LAPF_UI;
+
+ msg->l3h = msgb_put(msg, sizeof(*qh));
+ qh = (struct q933_a_hdr *) msg->l3h;
+ qh->prot_disc = prot_disc;
+ qh->call_ref = LMI_Q933A_CALLREF;
+ qh->msg_type = msg_type;
+
+ return msg;
+}
+
+/* obtain the [next] transmit sequence number */
+static uint8_t link_get_tx_seq(struct osmo_fr_link *link)
+{
+ /* The {user equipment, network} increments the send sequence
+ * counter using modulo 256. The value zero is skipped. */
+ link->last_tx_seq++;
+ if (link->last_tx_seq == 0)
+ link->last_tx_seq++;
+
+ return link->last_tx_seq;
+}
+
+/* Append PVC Status IE according to Q.933 A.3.2 */
+static void msgb_put_link_int_verif(struct msgb *msg, struct osmo_fr_link *link)
+{
+ uint8_t link_int_tx[2];
+ link_int_tx[0] = link_get_tx_seq(link);
+ link_int_tx[1] = link->last_rx_seq;
+ msgb_tlv_put(msg, Q933_IEI_LINK_INT_VERIF, 2, link_int_tx);
+}
+
+static void dlc_destroy(struct osmo_fr_dlc *dlc)
+{
+ llist_del(&dlc->list);
+ talloc_free(dlc);
+}
+
+/* Append PVC Status IE according to Q.933 A.3.3 */
+static void msgb_put_pvc_status(struct msgb *msg, struct osmo_fr_dlc *dlc)
+{
+ uint8_t ie[3];
+
+ ie[0] = (dlc->dlci >> 4) & 0x3f;
+ /* extension bits */
+ ie[1] = 0x80 | ((dlc->dlci & 0xf) << 3);
+ /* extension bits */
+ ie[2] = 0x80;
+
+ /* FIXME: validate: this status should be added as long it's not yet acked by the remote */
+ if (dlc->active)
+ ie[2] |= Q933_PVC_STATUS_DLC_ACTIVE;
+
+ if (dlc->add) {
+ ie[2] |= Q933_PVC_STATUS_DLC_NEW;
+ /* we've reported it as new once, reset the status */
+ }
+
+ if (dlc->del) {
+ ie[2] |= Q933_PVC_STATUS_DLC_DELETE;
+ /* we've reported it as deleted once, destroy it */
+ dlc_destroy(dlc);
+ }
+
+ msgb_tlv_put(msg, Q933_IEI_PVC_STATUS, 3, ie);
+}
+
+/* Send a Q.933 STATUS ENQUIRY given type over given link */
+static int tx_lmi_q933_status_enq(struct osmo_fr_link *link, uint8_t rep_type)
+{
+ struct msgb *resp;
+
+ resp = q933_msgb_alloc(0, Q931_PDISC_CC, Q931_MSGT_STATUS_ENQUIRY);
+ if (!resp)
+ return -1;
+ resp->dst = link;
+ link->expected_rep = rep_type;
+
+ /* Table A.2/Q.933 */
+ msgb_tlv_put(resp, Q933_IEI_REPORT_TYPE, 1, &rep_type);
+ msgb_put_link_int_verif(resp, link);
+
+ return link->tx_cb(link->tx_cb_data, resp);
+}
+
+/* Send a Q.933 STATUS of given type over given link */
+static int tx_lmi_q933_status(struct osmo_fr_link *link, uint8_t rep_type)
+{
+ struct osmo_fr_dlc *dlc;
+ struct msgb *resp;
+
+ resp = q933_msgb_alloc(0, Q931_PDISC_CC, Q931_MSGT_STATUS);
+ if (!resp)
+ return -1;
+
+ resp->dst = link;
+
+ /* Table A.1/Q.933 */
+ msgb_tlv_put(resp, Q933_IEI_REPORT_TYPE, 1, &rep_type);
+ switch (rep_type) {
+ case Q933_REPT_FULL_STATUS:
+ msgb_put_link_int_verif(resp, link);
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (dlc->add || dlc->del)
+ dlc->state_send = true;
+
+ msgb_put_pvc_status(resp, dlc);
+ }
+ break;
+ case Q933_REPT_LINK_INTEGRITY_VERIF:
+ msgb_put_link_int_verif(resp, link);
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (dlc->add || dlc->del) {
+ msgb_put_pvc_status(resp, dlc);
+ dlc->state_send = true;
+ }
+ }
+ break;
+ case Q933_REPT_SINGLE_PVC_ASYNC_STS:
+ llist_for_each_entry(dlc, &link->dlc_list, list)
+ msgb_put_pvc_status(resp, dlc);
+ break;
+ }
+
+ return link->tx_cb(link->tx_cb_data, resp);
+}
+
+
+/* Q.933 */
+static int rx_lmi_q933_status_enq(struct msgb *msg, struct tlv_parsed *tp)
+{
+ struct osmo_fr_link *link = msg->dst;
+ struct osmo_fr_dlc *dlc;
+ const uint8_t *link_int_rx;
+ uint8_t rep_type;
+
+ OSMO_ASSERT(link);
+
+ if (link->role == FR_ROLE_USER_EQUIPMENT) {
+ LOGPFRL(link, LOGL_ERROR, "STATUS-ENQ aren't supported in role user\n");
+ return -1;
+ }
+
+ /* check for mandatory IEs */
+ if (!TLVP_PRES_LEN(tp, Q933_IEI_REPORT_TYPE, 1) ||
+ !TLVP_PRES_LEN(tp, Q933_IEI_LINK_INT_VERIF, 2))
+ return -1;
+
+ rep_type = *TLVP_VAL(tp, Q933_IEI_REPORT_TYPE);
+
+ link_int_rx = TLVP_VAL(tp, Q933_IEI_LINK_INT_VERIF);
+ link->last_rx_seq = link_int_rx[0];
+
+ /* the network checks the receive sequence number received from
+ * the user equipment against its send sequence counter */
+ if (link_int_rx[1] != link->last_tx_seq) {
+ check_link_state(link, false);
+ link->err_count++;
+ } else {
+ check_link_state(link, true);
+ /* confirm DLC state changes */
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (!dlc->state_send)
+ continue;
+
+ if (dlc->add) {
+ dlc->active = link->state;
+ dlc->add = false;
+ }
+
+ if (dlc->del) {
+ dlc->del = false;
+ }
+
+ dlc->state_send = false;
+ }
+ }
+
+
+ /* The network responds to each STATUS ENQUIRY message with a
+ * STATUS message and resets the T392 timer */
+ osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);
+
+ return tx_lmi_q933_status(link, rep_type);
+}
+
+/* check if the link become active.
+ * The link becomes active when enough times a STATUS/STATUS ENQUIRY arrives without any loss.
+ * Look at the last N393 STATUS/STATUS ENQUIRY PDUs. The link is valid if at least N392
+ * got received.
+ * param[in] valid contains the status of the last packet */
+static void check_link_state(struct osmo_fr_link *link, bool valid)
+{
+ unsigned int last, i;
+ unsigned int carry = 0;
+ struct osmo_fr_dlc *dlc;
+
+ link->succeed <<= 1;
+ if (valid)
+ link->succeed |= 1;
+
+ /* count the bits */
+ last = link->succeed & ((1 << link->net->n393) - 1);
+ for (i = 0; i < link->net->n393; i++)
+ if (last & (1 << i))
+ carry++;
+
+ if (link->net->n393 - carry >= link->net->n392) {
+ /* failing link */
+ if (!link->state)
+ return;
+
+ LOGPFRL(link, LOGL_NOTICE, "Link failed\n");
+ link->state = false;
+ if (link->role == FR_ROLE_USER_EQUIPMENT)
+ return;
+
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ dlc->active = false;
+ }
+ } else {
+ /* good link */
+ if (link->state)
+ return;
+
+ LOGPFRL(link, LOGL_NOTICE, "Link recovered\n");
+ link->state = true;
+ if (link->role == FR_ROLE_USER_EQUIPMENT)
+ return;
+
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (!dlc->add && !dlc->del)
+ dlc->active = true;
+ }
+ }
+}
+
+static int validate_pvc_status(struct tlv_parsed *tp, size_t tp_len)
+{
+ size_t i;
+ uint16_t len = 0;
+
+ for (i = 0; i < tp_len; i++) {
+ if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))
+ continue;
+
+ /* PVC status can be 2 or 3 bytes. If the PVC is bigger
+ * ignore this to be compatible to future extensions. */
+ len = TLVP_LEN(&tp[i], Q933_IEI_PVC_STATUS);
+ if (len <= 1) {
+ return -EINVAL;
+ }
+ /* FIXME: validate correct flags: are some flags invalid at the same time? */
+ }
+
+ return 0;
+}
+
+static int parse_full_pvc_status(struct osmo_fr_link *link, struct tlv_parsed *tp, size_t tp_len)
+{
+ size_t i;
+ int err = 0;
+ struct osmo_fr_dlc *dlc, *tmp;
+ struct q933_a_pvc_sts *pvc;
+ uint16_t dlci = 0;
+ uint16_t *dlcis = talloc_zero_array(link, uint16_t, tp_len);
+ if (!dlcis)
+ return -ENOMEM;
+
+ /* first run validate all PVCs */
+ err = validate_pvc_status(tp, tp_len);
+ if (err < 0)
+ goto out;
+
+ for (i = 0; i < tp_len; i++) {
+ if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))
+ continue;
+
+ /* parse only 3 byte PVCs */
+ pvc = (struct q933_a_pvc_sts *) TLVP_VAL_MINLEN(
+ &tp[i],
+ Q933_IEI_PVC_STATUS,
+ sizeof(struct q933_a_pvc_sts));
+ if (!pvc)
+ continue;
+
+ dlci = ((pvc->dlci_msb & 0x3f) << 4) | (pvc->dlci_lsb & 0xf);
+ dlcis[i] = dlci;
+ dlc = osmo_fr_dlc_by_dlci(link, dlci);
+ if (!dlc) {
+ dlc = osmo_fr_dlc_alloc(link, dlci);
+ if (!dlc) {
+ LOGPFRL(link, LOGL_ERROR, "Could not create DLC %d\n", dlci);
+ continue;
+ }
+ }
+
+ /* Figure A.3/Q.933: The delete bit is only applicable for timely notification
+ * using the optional single PVC asynchronous status report.
+ * Ignoring the delete. */
+ dlc->add = pvc->new;
+ dlc->active = pvc->active;
+ dlc->del = 0;
+ }
+
+ /* check if all dlc are present in PVC Status */
+ llist_for_each_entry_safe(dlc, tmp, &link->dlc_list, list) {
+ bool found = false;
+ for (i = 0; i < tp_len; i++) {
+ if (dlcis[i] == dlc->dlci) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dlc->active = false;
+ dlc->del = true;
+ }
+ }
+
+ return 0;
+out:
+ talloc_free(dlcis);
+ return err;
+}
+
+static int parse_link_pvc_status(struct osmo_fr_link *link, struct tlv_parsed *tp, size_t tp_len)
+{
+ int err;
+ size_t i;
+ struct q933_a_pvc_sts *pvc;
+ struct osmo_fr_dlc *dlc;
+ uint16_t dlci = 0;
+
+ err = validate_pvc_status(tp, tp_len);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < tp_len; i++) {
+ if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))
+ continue;
+
+ /* parse only 3 byte PVCs */
+ pvc = (struct q933_a_pvc_sts *) TLVP_VAL_MINLEN(
+ &tp[i],
+ Q933_IEI_PVC_STATUS,
+ sizeof(struct q933_a_pvc_sts));
+ if (!pvc)
+ continue;
+
+ dlci = ((pvc->dlci_msb & 0x3f) << 4) | (pvc->dlci_lsb & 0xf);
+ dlc = osmo_fr_dlc_by_dlci(link, dlci);
+ if (!dlc) {
+ /* don't create dlc's for the ones which are about to be deleted. */
+ if (pvc->delete)
+ continue;
+
+ dlc = osmo_fr_dlc_alloc(link, dlci);
+ if (!dlc) {
+ LOGPFRL(link, LOGL_ERROR, "Rx STATUS: Could not create DLC %d\n", dlci);
+ }
+ }
+
+ if (pvc->delete) {
+ dlc->del = 1;
+ } else {
+ dlc->add = pvc->new;
+ dlc->active = pvc->active;
+ dlc->del = 0;
+ }
+ }
+
+ return 0;
+}
+
+static size_t count_pvc_status(struct tlv_parsed *tp, size_t tp_len)
+{
+ size_t i, count = 0;
+ for (i = 0; i < tp_len; i++) {
+ if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))
+ continue;
+ count++;
+ }
+
+ return count;
+}
+
+static int rx_lmi_q933_status(struct msgb *msg, struct tlv_parsed *tp)
+{
+ struct osmo_fr_link *link = msg->dst;
+ const uint8_t *link_int_rx;
+ uint8_t rep_type;
+
+ OSMO_ASSERT(link);
+
+ if (link->role == FR_ROLE_NETWORK_EQUIPMENT) {
+ LOGPFRL(link, LOGL_ERROR, "Rx STATUS: STATUS aren't supported in role network\n");
+ return -1;
+ }
+
+ /* check for mandatory IEs */
+ if (!TLVP_PRES_LEN(tp, Q933_IEI_REPORT_TYPE, 1)) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx STATUSL: Missing TLV Q933 Report Type\n");
+ return -1;
+ }
+
+ rep_type = *TLVP_VAL(tp, Q933_IEI_REPORT_TYPE);
+
+ switch (rep_type) {
+ case Q933_REPT_FULL_STATUS:
+ case Q933_REPT_LINK_INTEGRITY_VERIF:
+ if (rep_type != link->expected_rep) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx STATUS: Unexpected Q933 report type (got 0x%x != exp 0x%x)\n",
+ rep_type, link->expected_rep);
+ return -1;
+ }
+
+ if (!TLVP_PRES_LEN(tp, Q933_IEI_LINK_INT_VERIF, 2)) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx STATUS: Missing TLV Q933 Link Integrety Verification\n");
+ return -1;
+ }
+ link_int_rx = TLVP_VAL(tp, Q933_IEI_LINK_INT_VERIF);
+ link->last_rx_seq = link_int_rx[0];
+ /* The received receive sequence number is not valid if
+ * it is not equal to the last transmitted send sequence
+ * number. Ignore messages containing this error. As a
+ * result, timer T391 expires and the user then
+ * increments the error count. */
+ if (link_int_rx[1] != link->last_tx_seq)
+ return 0;
+ break;
+ case Q933_REPT_SINGLE_PVC_ASYNC_STS:
+ default:
+ return -1;
+ }
+
+ check_link_state(link, true);
+ if (count_pvc_status(tp, MAX_SUPPORTED_PVC + 1) > MAX_SUPPORTED_PVC) {
+ LOGPFRL(link, LOGL_ERROR, "Rx STATUS: Too many PVC! Only %d are supported!\n", MAX_SUPPORTED_PVC);
+ }
+
+ switch (rep_type) {
+ case Q933_REPT_FULL_STATUS:
+ parse_full_pvc_status(link, tp, MAX_SUPPORTED_PVC);
+ break;
+ case Q933_REPT_LINK_INTEGRITY_VERIF:
+ parse_link_pvc_status(link, tp, MAX_SUPPORTED_PVC);
+ break;
+ default:
+ break;
+ }
+
+ /* The network responds to each STATUS ENQUIRY message with a
+ * STATUS message and resets the T392 timer */
+ osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);
+
+ return 0;
+}
+
+static int rx_lmi_q922(struct msgb *msg)
+{
+ struct osmo_fr_link *link = msg->dst;
+ struct q933_a_hdr *qh;
+ /* the + 1 is used to detect more than MAX_SUPPORTED_PVC */
+ struct tlv_parsed tp[MAX_SUPPORTED_PVC + 1];
+ uint8_t *lapf;
+ int rc;
+
+ OSMO_ASSERT(link);
+
+ if (msgb_l2len(msg) < 1)
+ return -1;
+ lapf = msgb_l2(msg);
+
+ /* we only support LAPF UI frames */
+ if (lapf[0] != LAPF_UI)
+ return -1;
+
+ msg->l3h = msg->l2h + 1;
+ if (msgb_l3len(msg) < 3)
+ return -1;
+
+ qh = (struct q933_a_hdr *) msgb_l3(msg);
+ if (qh->prot_disc != Q931_PDISC_CC) {
+ LOGPFRL(link, LOGL_NOTICE,
+ "Rx unsupported LMI protocol discriminator %u\n", qh->prot_disc);
+ return -1;
+ }
+
+ rc = tlv_parse2(tp, MAX_SUPPORTED_PVC + 1, &q933_att_tlvdef,
+ msgb_l3(msg) + sizeof(*qh),
+ msgb_l3len(msg) - sizeof(*qh), 0, 0);
+ if (rc < 0) {
+ LOGPFRL(link, LOGL_NOTICE,
+ "Failed to parse TLVs in LMI message type %u\n", qh->msg_type);
+ return rc;
+ }
+
+ switch (qh->msg_type) {
+ case Q931_MSGT_STATUS_ENQUIRY:
+ rc = rx_lmi_q933_status_enq(msg, tp);
+ break;
+ case Q931_MSGT_STATUS:
+ rc = rx_lmi_q933_status(msg, tp);
+ break;
+ default:
+ LOGPFRL(link, LOGL_NOTICE,
+ "Rx unsupported LMI message type %u\n", qh->msg_type);
+ rc = -1;
+ break;
+ }
+ msgb_free(msg);
+
+ return rc;
+}
+
+int osmo_fr_rx(struct msgb *msg)
+{
+ int rc = 0;
+ uint8_t *frh;
+ uint16_t dlci;
+ struct osmo_fr_dlc *dlc;
+ struct osmo_fr_link *link = msg->dst;
+
+ OSMO_ASSERT(link);
+
+ if (msgb_length(msg) < 2) {
+ LOGPFRL(link, LOGL_ERROR, "Rx short FR header: %u bytes\n", msgb_length(msg));
+ rc = -1;
+ goto out;
+ }
+
+ frh = msg->l1h = msgb_data(msg);
+ if (frh[0] & 0x01) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx Unsupported single-byte FR address\n");
+ rc = -1;
+ goto out;
+ }
+ if ((frh[1] & 0x0f) != 0x01) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx Unknown second FR octet 0x%02x\n", frh[1]);
+ rc = -1;
+ goto out;
+ }
+ dlci = q922_to_dlci(frh);
+ msg->l2h = frh + 2;
+
+ switch (dlci) {
+ case LMI_Q933A_DLCI:
+ return rx_lmi_q922(msg);
+ case LMI_CISCO_DLCI:
+ LOGPFRL(link, LOGL_ERROR, "Rx Unsupported FR DLCI %u\n", dlci);
+ goto out;
+ }
+
+ if (!link->state) {
+ LOGPFRL(link, LOGL_NOTICE, "Link is not reliable. Discarding Rx PDU on DLCI %d\n", dlci);
+ goto out;
+ }
+
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (dlc->dlci == dlci) {
+ /* dispatch to handler of respective DLC */
+ msg->dst = dlc;
+ return dlc->rx_cb(dlc->rx_cb_data, msg);
+ }
+ }
+
+ if (link->unknown_dlc_rx_cb)
+ return link->unknown_dlc_rx_cb(link->unknown_dlc_rx_cb_data, msg);
+ else
+ LOGPFRL(link, LOGL_NOTICE, "DLCI %u doesn't exist, discarding\n", dlci);
+
+out:
+ msgb_free(msg);
+
+ return rc;
+}
+
+int osmo_fr_tx_dlc(struct msgb *msg)
+{
+ uint8_t *frh;
+ struct osmo_fr_dlc *dlc = msg->dst;
+ struct osmo_fr_link *link = dlc->link;
+
+ OSMO_ASSERT(dlc);
+ OSMO_ASSERT(link);
+
+ if (!link->state) {
+ LOGPFRL(link, LOGL_NOTICE, "Link is not reliable (yet), discarding Tx\n");
+ msgb_free(msg);
+ return -1;
+ }
+ if (!dlc->active) {
+ LOGPFRL(link, LOGL_NOTICE, "DLCI %u is not active (yet), discarding Tx\n", dlc->dlci);
+ msgb_free(msg);
+ return -1;
+ }
+ LOGPFRL(link, LOGL_DEBUG, "DLCI %u is active, sending message\n", dlc->dlci);
+
+ if (msgb_headroom(msg) < 2) {
+ msgb_free(msg);
+ return -ENOSPC;
+ }
+
+ frh = msgb_push(msg, 2);
+ dlci_to_q922(frh, dlc->dlci);
+
+ msg->dst = link;
+ return link->tx_cb(link->tx_cb_data, msg);
+}
+
+/* Every T391 seconds, the user equipment sends a STATUS ENQUIRY
+ * message to the network and resets its polling timer (T391). */
+static void fr_t391_cb(void *data)
+{
+ struct osmo_fr_link *link = data;
+
+ OSMO_ASSERT(link);
+
+ if (link->polling_count % link->net->n391 == 0)
+ tx_lmi_q933_status_enq(link, Q933_REPT_FULL_STATUS);
+ else
+ tx_lmi_q933_status_enq(link, Q933_REPT_LINK_INTEGRITY_VERIF);
+ link->polling_count++;
+ osmo_timer_schedule(&link->t391, osmo_tdef_get(link->net->T_defs, 391, OSMO_TDEF_S, 10), 0);
+}
+
+static void fr_t392_cb(void *data)
+{
+ struct osmo_fr_link *link = data;
+
+ OSMO_ASSERT(link);
+
+ /* A.5 The network increments the error count .. Non-receipt of
+ * a STATUS ENQUIRY within T392, which results in restarting
+ * T392 */
+ link->err_count++;
+ check_link_state(link, false);
+ osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);
+}
+
+/* allocate a frame relay network */
+struct osmo_fr_network *osmo_fr_network_alloc(void *ctx)
+{
+ struct osmo_fr_network *net = talloc_zero(ctx, struct osmo_fr_network);
+
+ INIT_LLIST_HEAD(&net->links);
+ net->T_defs = fr_tdefs;
+ osmo_tdefs_reset(net->T_defs);
+ net->n391 = 6;
+ net->n392 = 3;
+ net->n393 = 4;
+
+ return net;
+}
+
+void osmo_fr_network_free(struct osmo_fr_network *net)
+{
+ struct osmo_fr_link *link, *tmp;
+
+ if (!net)
+ return;
+
+ llist_for_each_entry_safe(link, tmp, &net->links, list) {
+ osmo_fr_link_free(link);
+ }
+}
+
+/* allocate a frame relay link in a given network */
+struct osmo_fr_link *osmo_fr_link_alloc(struct osmo_fr_network *net, enum osmo_fr_role role, const char *name)
+{
+ struct osmo_fr_link *link = talloc_zero(net, struct osmo_fr_link);
+ if (!link)
+ return NULL;
+
+ LOGPFRL(link, LOGL_INFO, "Creating frame relay link with role %s\n", osmo_fr_role_str(role));
+
+ link->role = role;
+ link->net = net;
+ link->name = talloc_strdup(link, name);
+ INIT_LLIST_HEAD(&link->dlc_list);
+ llist_add_tail(&link->list, &net->links);
+
+ osmo_timer_setup(&link->t391, fr_t391_cb, link);
+ osmo_timer_setup(&link->t392, fr_t392_cb, link);
+
+ switch (role) {
+ case FR_ROLE_USER_EQUIPMENT:
+ osmo_timer_schedule(&link->t391, osmo_tdef_get(link->net->T_defs, 391, OSMO_TDEF_S, 15), 0);
+ break;
+ case FR_ROLE_NETWORK_EQUIPMENT:
+ osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);
+ break;
+ }
+
+ return link;
+}
+
+void osmo_fr_link_free(struct osmo_fr_link *link)
+{
+ struct osmo_fr_dlc *dlc, *tmp;
+
+ if (!link)
+ return;
+
+ osmo_timer_del(&link->t391);
+ osmo_timer_del(&link->t392);
+
+ llist_for_each_entry_safe(dlc, tmp, &link->dlc_list, list) {
+ osmo_fr_dlc_free(dlc);
+ }
+
+ llist_del(&link->list);
+ talloc_free(link);
+}
+
+/* allocate a data link connectoin on a given framerelay link */
+struct osmo_fr_dlc *osmo_fr_dlc_alloc(struct osmo_fr_link *link, uint16_t dlci)
+{
+ struct osmo_fr_dlc *dlc = talloc_zero(link, struct osmo_fr_dlc);
+ if (!dlc)
+ return NULL;
+
+ dlc->link = link;
+ dlc->dlci = dlci;
+ dlc->active = false;
+
+ llist_add_tail(&dlc->list, &link->dlc_list);
+
+ dlc->add = true;
+ tx_lmi_q933_status(link, Q933_IEI_PVC_STATUS);
+
+ return dlc;
+}
+
+void osmo_fr_dlc_free(struct osmo_fr_dlc *dlc)
+{
+ llist_del(&dlc->list);
+ talloc_free(dlc);
+}
+
+/* TODO: rework osmo_fr_dlc_alloc/free with handling it's own memory.
+ * For network role: The dlc have to created by the application (e.g. vty).
+ * The dlc shouldn't free'd directly. It should be communicated to the
+ * other side and wait until it's confirmed OR the link go off and free it afterwards.
+ * For user equpment role: The dlc can be created by the application or the dlc will be created
+ * by the frame relay because the network is configuring the dlc.
+ * The dlc shouldn't be free'd. Only the handler should be set to NULL.
+ */
+
+struct osmo_fr_dlc *osmo_fr_dlc_by_dlci(struct osmo_fr_link *link, uint16_t dlci)
+{
+ struct osmo_fr_dlc *dlc;
+
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (dlc->dlci == dlci)
+ return dlc;
+ }
+ return NULL;
+}
diff --git a/src/gb/gprs_bssgp.c b/src/gb/gprs_bssgp.c
index 8b8d534e..7fb3a302 100644
--- a/src/gb/gprs_bssgp.c
+++ b/src/gb/gprs_bssgp.c
@@ -41,6 +41,7 @@
#include <osmocom/gprs/gprs_ns.h>
#include "common_vty.h"
+#include "osmocom/gsm/gsm48.h"
void *bssgp_tall_ctx = NULL;
@@ -92,6 +93,39 @@ struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t
return NULL;
}
+/*! Transmit a BVC-RESET message with a given nsei and bvci (Chapter 10.4.12)
+ * \param[in] nsei The NSEI to transmit over
+ * \param[in] bvci BVCI of the BVC to reset
+ * \param[in] cause The cause of the reset
+ * \param[in] ra_id Pointer to the ra_id to include. If NULL no cell information will be included
+ * \param[in] cell_id The cell_id to include (if ra_id is not NULL)
+ */
+int bssgp_tx_bvc_reset_nsei_bvci(uint16_t nsei, uint16_t bvci, enum gprs_bssgp_cause cause, const struct gprs_ra_id *ra_id, uint16_t cell_id)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph =
+ (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ uint16_t _bvci = osmo_htons(bvci);
+
+ msgb_nsei(msg) = nsei;
+ msgb_bvci(msg) = 0; /* Signalling */
+ bgph->pdu_type = BSSGP_PDUT_BVC_RESET;
+ LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-RESET "
+ "CAUSE=%s\n", bvci, bssgp_cause_str(cause));
+
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, (uint8_t *) &cause);
+ if (ra_id) {
+ uint8_t bssgp_cid[8];
+ bssgp_create_cell_id(bssgp_cid, ra_id, cell_id);
+ msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
+ }
+
+ /* Optional: Feature Bitmap */
+
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
+}
+
/*! Initiate reset procedure for all PTP BVC on a given NSEI.
*
* This function initiates reset procedure for all PTP BVC with a given cause.
@@ -317,7 +351,7 @@ static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
/* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
* informs us about its RAC + Cell ID, so we can create a mapping */
if (bvci != 0 && bvci != 1) {
- if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_CELL_ID, 8)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESET "
"missing mandatory IE\n", bvci);
return -EINVAL;
@@ -433,7 +467,7 @@ static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x Rx UPLINK-UNITDATA\n", msgb_tlli(msg));
/* Cell ID and LLC_PDU are the only mandatory IE */
- if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) ||
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_CELL_ID, 8) ||
!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
"missing mandatory IE\n", msgb_tlli(msg));
@@ -463,8 +497,8 @@ static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp)
uint16_t ns_bvci = msgb_bvci(msg), nsei = msgb_nsei(msg);
int rc;
- if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_TLLI, 4) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
"missing mandatory IE\n", ns_bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
@@ -504,9 +538,9 @@ static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp)
uint16_t ns_bvci = msgb_bvci(msg), nsei = msgb_nsei(msg);
int rc;
- if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
- !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_TLLI, 4 ) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_SUSPEND_REF_NR, 1)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
"missing mandatory IE\n", ns_bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
@@ -546,16 +580,16 @@ static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
uint32_t tlli = 0;
uint16_t nsei = msgb_nsei(msg);
- if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
- !TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_TLLI, 4) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_LLC_FRAMES_DISCARDED, 1) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_NUM_OCT_AFF, 3)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
"missing mandatory IE\n", ctx->bvci);
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
- if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
- tlli = tlvp_val32be(tp, BSSGP_IE_TLLI);
+ tlli = tlvp_val32be(tp, BSSGP_IE_TLLI);
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n",
ctx->bvci, tlli);
@@ -581,7 +615,7 @@ int bssgp_rx_status(struct msgb *msg, struct tlv_parsed *tp,
struct osmo_bssgp_prim nmp;
enum gprs_bssgp_cause cause;
- if (!TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_CAUSE, 1)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx STATUS "
"missing mandatory IE\n", bvci);
cause = BSSGP_CAUSE_PROTO_ERR_UNSPEC;
@@ -593,7 +627,7 @@ int bssgp_rx_status(struct msgb *msg, struct tlv_parsed *tp,
bvci, bssgp_cause_str(cause));
if (cause == BSSGP_CAUSE_BVCI_BLOCKED || cause == BSSGP_CAUSE_UNKNOWN_BVCI) {
- if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI))
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2))
LOGP(DBSSGP, LOGL_ERROR,
"BSSGP BVCI=%u Rx STATUS cause=%s "
"missing conditional BVCI IE\n",
@@ -848,11 +882,11 @@ static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
bctx->bvci);
- if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
- !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
- !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
- !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
- !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_TAG, 1) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_BVC_BUCKET_SIZE, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_BUCKET_LEAK_RATE, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_BMAX_DEFAULT_MS, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_R_DEFAULT_MS,2)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
"missing mandatory IE\n", bctx->bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
@@ -1006,8 +1040,8 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
break;
case BSSGP_PDUT_BVC_BLOCK:
/* BSS tells us that BVC shall be blocked */
- if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_CAUSE, 1)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
"missing mandatory IE\n");
goto err_mand_ie;
@@ -1016,7 +1050,7 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
break;
case BSSGP_PDUT_BVC_UNBLOCK:
/* BSS tells us that BVC shall be unblocked */
- if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
"missing mandatory IE\n");
goto err_mand_ie;
@@ -1028,8 +1062,8 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
break;
case BSSGP_PDUT_BVC_RESET:
/* BSS tells us that BVC init is required */
- if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_CAUSE, 1)) {
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
"missing mandatory IE\n");
goto err_mand_ie;
@@ -1101,7 +1135,7 @@ int bssgp_rcvmsg(struct msgb *msg)
return rc;
}
- if (bvci == BVCI_SIGNALLING && TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
+ if (bvci == BVCI_SIGNALLING && TLVP_PRES_LEN(&tp, BSSGP_IE_BVCI, 2))
bvci = tlvp_val16be(&tp, BSSGP_IE_BVCI);
/* look-up or create the BTS context for this BVC */
diff --git a/src/gb/gprs_bssgp_bss.c b/src/gb/gprs_bssgp_bss.c
index d1734ee5..462666ad 100644
--- a/src/gb/gprs_bssgp_bss.c
+++ b/src/gb/gprs_bssgp_bss.c
@@ -293,28 +293,10 @@ int bssgp_tx_bvc_unblock(struct bssgp_bvc_ctx *bctx)
/*! Transmit a BVC-RESET message (Chapter 10.4.12) */
int bssgp_tx_bvc_reset2(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause, bool add_cell_id)
{
- struct msgb *msg = bssgp_msgb_alloc();
- struct bssgp_normal_hdr *bgph =
- (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
- uint16_t _bvci = osmo_htons(bvci);
-
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-RESET "
- "CAUSE=%s\n", bvci, bssgp_cause_str(cause));
-
- msgb_nsei(msg) = bctx->nsei;
- msgb_bvci(msg) = 0; /* Signalling */
- bgph->pdu_type = BSSGP_PDUT_BVC_RESET;
-
- msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
- msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
- if (add_cell_id) {
- uint8_t bssgp_cid[8];
- bssgp_create_cell_id(bssgp_cid, &bctx->ra_id, bctx->cell_id);
- msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
- }
- /* Optional: Feature Bitmap */
-
- return bssgp_ns_send(bssgp_ns_send_data, msg);
+ if (add_cell_id)
+ return bssgp_tx_bvc_reset_nsei_bvci(bctx->nsei, bvci, cause, &bctx->ra_id, bctx->cell_id);
+ else
+ return bssgp_tx_bvc_reset_nsei_bvci(bctx->nsei, bvci, cause, NULL, 0);
}
int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause)
{
@@ -529,24 +511,24 @@ int bssgp_rx_paging(struct bssgp_paging_info *pinfo,
TLVP_LEN(&tp, BSSGP_IE_IMSI));
/* DRX Parameters */
- if (!TLVP_PRESENT(&tp, BSSGP_IE_DRX_PARAMS))
+ if (!TLVP_PRES_LEN(&tp, BSSGP_IE_DRX_PARAMS, 2))
goto err_mand_ie;
pinfo->drx_params = tlvp_val16be(&tp, BSSGP_IE_DRX_PARAMS);
/* Scope */
- if (TLVP_PRESENT(&tp, BSSGP_IE_BSS_AREA_ID)) {
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_BSS_AREA_ID, 1)) {
pinfo->scope = BSSGP_PAGING_BSS_AREA;
- } else if (TLVP_PRESENT(&tp, BSSGP_IE_LOCATION_AREA)) {
+ } else if (TLVP_PRES_LEN(&tp, BSSGP_IE_LOCATION_AREA, 5)) {
pinfo->scope = BSSGP_PAGING_LOCATION_AREA;
memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_LOCATION_AREA),
TLVP_LEN(&tp, BSSGP_IE_LOCATION_AREA));
gsm48_parse_ra(&pinfo->raid, ra);
- } else if (TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) {
+ } else if (TLVP_PRES_LEN(&tp, BSSGP_IE_ROUTEING_AREA, 6)) {
pinfo->scope = BSSGP_PAGING_ROUTEING_AREA;
memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA),
TLVP_LEN(&tp, BSSGP_IE_ROUTEING_AREA));
gsm48_parse_ra(&pinfo->raid, ra);
- } else if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) {
+ } else if (TLVP_PRES_LEN(&tp, BSSGP_IE_BVCI, 2)) {
pinfo->scope = BSSGP_PAGING_BVCI;
pinfo->bvci = tlvp_val16be(&tp, BSSGP_IE_BVCI);
} else
@@ -564,8 +546,7 @@ int bssgp_rx_paging(struct bssgp_paging_info *pinfo,
}
/* Optional (P-)TMSI */
- if (TLVP_PRESENT(&tp, BSSGP_IE_TMSI) &&
- TLVP_LEN(&tp, BSSGP_IE_TMSI) >= 4) {
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_TMSI, 4)) {
if (!pinfo->ptmsi)
pinfo->ptmsi = talloc_zero_size(pinfo, sizeof(uint32_t));
*(pinfo->ptmsi) = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TMSI));
diff --git a/src/gb/gprs_bssgp_util.c b/src/gb/gprs_bssgp_util.c
index 917f1f32..da0b1039 100644
--- a/src/gb/gprs_bssgp_util.c
+++ b/src/gb/gprs_bssgp_util.c
@@ -107,6 +107,8 @@ static const struct value_string bssgp_pdu_strings[] = {
{ BSSGP_PDUT_UL_UNITDATA, "UL-UNITDATA" },
{ BSSGP_PDUT_RA_CAPABILITY, "RA-CAPABILITY" },
{ BSSGP_PDUT_PTM_UNITDATA, "PTM-UNITDATA" },
+ { BSSGP_PDUT_DL_MMBS_UNITDATA, "DL-MBMS-UNITDATA" },
+ { BSSGP_PDUT_UL_MMBS_UNITDATA, "UL-MBMS-UNITDATA" },
{ BSSGP_PDUT_PAGING_PS, "PAGING-PS" },
{ BSSGP_PDUT_PAGING_CS, "PAGING-CS" },
{ BSSGP_PDUT_RA_CAPA_UDPATE, "RA-CAPABILITY-UPDATE" },
@@ -118,6 +120,10 @@ static const struct value_string bssgp_pdu_strings[] = {
{ BSSGP_PDUT_RESUME, "RESUME" },
{ BSSGP_PDUT_RESUME_ACK, "RESUME-ACK" },
{ BSSGP_PDUT_RESUME_NACK, "RESUME-NACK" },
+ { BSSGP_PDUT_DUMMY_PAGING_PS, "DUMMY-PAGING-PS" },
+ { BSSGP_PDUT_DUMMY_PAGING_PS_RESP, "DUMMY-PAGING-PS-RESP" },
+ { BSSGP_PDUT_MS_REGISTR_ENQ, "MS-REGISTRATION-ENQ" },
+ { BSSGP_PDUT_MS_REGISTR_ENQ_RESP, "MS-REGISTRATION-ENQ-RESP" },
{ BSSGP_PDUT_BVC_BLOCK, "BVC-BLOCK" },
{ BSSGP_PDUT_BVC_BLOCK_ACK, "BVC-BLOCK-ACK" },
{ BSSGP_PDUT_BVC_RESET, "BVC-RESET" },
@@ -131,8 +137,11 @@ static const struct value_string bssgp_pdu_strings[] = {
{ BSSGP_PDUT_FLUSH_LL, "FLUSH-LL" },
{ BSSGP_PDUT_FLUSH_LL_ACK, "FLUSH-LL-ACK" },
{ BSSGP_PDUT_LLC_DISCARD, "LLC DISCARDED" },
+ { BSSGP_PDUT_FLOW_CONTROL_PFC, "FLOW-CONTROL-PFC" },
+ { BSSGP_PDUT_FLOW_CONTROL_PFC_ACK, "FLOW-CONTROL-PFC-ACK" },
{ BSSGP_PDUT_SGSN_INVOKE_TRACE, "SGSN-INVOKE-TRACE" },
{ BSSGP_PDUT_STATUS, "STATUS" },
+ { BSSGP_PDUT_OVERLOAD, "OVERLOAD" },
{ BSSGP_PDUT_DOWNLOAD_BSS_PFC, "DOWNLOAD-BSS-PFC" },
{ BSSGP_PDUT_CREATE_BSS_PFC, "CREATE-BSS-PFC" },
{ BSSGP_PDUT_CREATE_BSS_PFC_ACK, "CREATE-BSS-PFC-ACK" },
@@ -141,6 +150,16 @@ static const struct value_string bssgp_pdu_strings[] = {
{ BSSGP_PDUT_MODIFY_BSS_PFC_ACK, "MODIFY-BSS-PFC-ACK" },
{ BSSGP_PDUT_DELETE_BSS_PFC, "DELETE-BSS-PFC" },
{ BSSGP_PDUT_DELETE_BSS_PFC_ACK, "DELETE-BSS-PFC-ACK" },
+ { BSSGP_PDUT_DELETE_BSS_PFC_REQ, "DELETE-BSS-PFC-REQ" },
+ { BSSGP_PDUT_PS_HO_REQUIRED, "PS-HO-REQUIRED" },
+ { BSSGP_PDUT_PS_HO_REQUIRED_ACK, "PS-HO-REQUIRED-ACK" },
+ { BSSGP_PDUT_PS_HO_REQUIRED_NACK, "PS-HO-REQUIRED-NACK" },
+ { BSSGP_PDUT_PS_HO_REQUEST, "PS-HO-REQUEST" },
+ { BSSGP_PDUT_PS_HO_REQUEST_ACK, "PS-HO-REQUEST-ACK" },
+ { BSSGP_PDUT_PS_HO_REQUEST_NACK, "PS-HO-REQUEST-NACK" },
+ { BSSGP_PDUT_PS_HO_COMPLETE, "PS-HO-COMPLETE" },
+ { BSSGP_PDUT_PS_HO_CANCEL, "PS-HO-CANCEL" },
+ { BSSGP_PDUT_PS_HO_COMPLETE_ACK, "PS-HO-COMPLETE-ACK" },
{ 0, NULL },
};
diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c
index f65bea7a..d90ba850 100644
--- a/src/gb/gprs_ns2.c
+++ b/src/gb/gprs_ns2.c
@@ -57,9 +57,7 @@
* Those mappings are administratively configured.
*
* This implementation has the following limitations:
- * - Only one NS-VC for each NSE: No load-sharing function
* - NSVCI 65535 and 65534 are reserved for internal use
- * - Only UDP is supported as of now, no frame relay support
* - There are no BLOCK and UNBLOCK timers (yet?)
*
* \file gprs_ns2.c */
@@ -74,6 +72,7 @@
#include <arpa/inet.h>
#include <osmocom/core/fsm.h>
+#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/socket.h>
@@ -138,7 +137,7 @@ static const struct tlv_definition ns_att_tlvdef2 = {
/* Section 10.3.2, Table 13 */
-static const struct value_string ns2_cause_str[] = {
+const struct value_string gprs_ns2_cause_strs[] = {
{ NS_CAUSE_TRANSIT_FAIL, "Transit network failure" },
{ NS_CAUSE_OM_INTERVENTION, "O&M intervention" },
{ NS_CAUSE_EQUIP_FAIL, "Equipment failure" },
@@ -160,13 +159,6 @@ static const struct value_string ns2_cause_str[] = {
{ 0, NULL }
};
-/*! Obtain a human-readable string for NS cause value */
-const char *gprs_ns2_cause_str(int cause)
-{
- enum ns_cause _cause = cause;
- return get_value_string(ns2_cause_str, _cause);
-}
-
static const struct rate_ctr_desc nsvc_ctr_description[] = {
{ "packets:in", "Packets at NS Level ( In)" },
{ "packets:out","Packets at NS Level (Out)" },
@@ -203,6 +195,30 @@ static const struct osmo_stat_item_group_desc nsvc_statg_desc = {
.class_id = OSMO_STATS_CLASS_PEER,
};
+const struct value_string gprs_ns2_aff_cause_prim_strs[] = {
+ { NS_AFF_CAUSE_VC_FAILURE, "NSVC failure" },
+ { NS_AFF_CAUSE_VC_RECOVERY, "NSVC recovery" },
+ { NS_AFF_CAUSE_FAILURE, "NSE failure" },
+ { NS_AFF_CAUSE_RECOVERY, "NSE recovery" },
+ { NS_AFF_CAUSE_SNS_CONFIGURED, "NSE SNS configured" },
+ { NS_AFF_CAUSE_SNS_FAILURE, "NSE SNS failure" },
+ { 0, NULL }
+};
+
+const struct value_string gprs_ns2_prim_strs[] = {
+ { PRIM_NS_UNIT_DATA, "UNIT DATA" },
+ { PRIM_NS_CONGESTION, "CONGESTION" },
+ { PRIM_NS_STATUS, "STATUS" },
+ { 0, NULL }
+};
+
+const struct value_string gprs_ns2_lltype_strs[] = {
+ { GPRS_NS2_LL_UDP, "UDP" },
+ { GPRS_NS2_LL_FR_GRE, "FR_GRE" },
+ { GPRS_NS2_LL_FR, "FR" },
+ { 0, NULL }
+};
+
/*! string-format a given NS-VC into a user-supplied buffer.
* \param[in] buf user-allocated output buffer
* \param[in] buf_len size of user-allocated output buffer in bytes
@@ -218,8 +234,8 @@ char *gprs_ns2_ll_str_buf(char *buf, size_t buf_len, struct gprs_ns2_vc *nsvc)
if (!buf_len)
return NULL;
- switch (nsvc->ll) {
- case GPRS_NS_LL_UDP:
+ switch (nsvc->nse->ll) {
+ case GPRS_NS2_LL_UDP:
if (!gprs_ns2_is_ip_bind(nsvc->bind)) {
buf[0] = '\0';
return buf;
@@ -242,14 +258,15 @@ char *gprs_ns2_ll_str_buf(char *buf, size_t buf_len, struct gprs_ns2_vc *nsvc)
local_str.ip, local_str.port,
remote_str.ip, remote_str.port);
break;
- case GPRS_NS_LL_FR_GRE:
+ case GPRS_NS2_LL_FR_GRE:
snprintf(buf, buf_len, "frgre)");
break;
- case GPRS_NS_LL_E1:
- snprintf(buf, buf_len, "e1)");
+ case GPRS_NS2_LL_FR:
+ snprintf(buf, buf_len, "fr)netif: %s dlci: %u", gprs_ns2_fr_bind_netif(nsvc->bind),
+ gprs_ns2_fr_nsvc_dlci(nsvc));
break;
default:
- buf[0] = '\0';
+ snprintf(buf, buf_len, "unknown)");
break;
}
@@ -282,19 +299,133 @@ char *gprs_ns2_ll_str_c(const void *ctx, struct gprs_ns2_vc *nsvc)
return gprs_ns2_ll_str_buf(buf, NS2_LL_MAX_STR, nsvc);
}
+/*! Return the current state name of a given NS-VC to a thread-local static buffer.
+ * \param[in] nsvc NS-VC to return the state of
+ * \return pointer to the string on success; NULL on error */
+const char *gprs_ns2_nsvc_state_name(struct gprs_ns2_vc *nsvc)
+{
+ return osmo_fsm_inst_state_name(nsvc->fi);
+}
+
+/* select a signalling NSVC and respect sig_counter
+ * param[out] reset_counter - all counter has to be resetted to their signal weight
+ * return the chosen nsvc or NULL
+ */
+static struct gprs_ns2_vc *ns2_load_sharing_signal(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc = NULL, *last = NULL, *tmp;
+
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (tmp->sig_weight == 0)
+ continue;
+ if (!gprs_ns2_vc_is_unblocked(tmp))
+ continue;
+ if (tmp->sig_counter == 0) {
+ last = tmp;
+ continue;
+ }
+
+ tmp->sig_counter--;
+ nsvc = tmp;
+ break;
+ }
+
+ /* all counter were zero, but there are valid nsvc */
+ if (!nsvc && last) {
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ tmp->sig_counter = tmp->sig_weight;
+ }
+
+ last->sig_counter--;
+ return last;
+ } else {
+ return nsvc;
+ }
+}
+
+/* 4.4.1 Load Sharing function for the Frame Relay Sub-Network */
+static struct gprs_ns2_vc *ns2_load_sharing_modulor(
+ struct gprs_ns2_nse *nse,
+ uint16_t bvci,
+ uint32_t load_selector)
+{
+ struct gprs_ns2_vc *tmp;
+ uint32_t mod = (bvci + load_selector) % nse->nsvc_data_count;
+ uint32_t i = 0;
+
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (!gprs_ns2_vc_is_unblocked(tmp))
+ continue;
+ if (tmp->data_weight == 0)
+ continue;
+
+ if (i == mod)
+ return tmp;
+ i++;
+ }
+
+ return NULL;
+}
+
+/* pick the first available data NSVC - no load sharing */
+struct gprs_ns2_vc *ns2_load_sharing_first(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc = NULL, *tmp;
+
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (!gprs_ns2_vc_is_unblocked(tmp))
+ continue;
+ if (tmp->data_weight == 0)
+ continue;
+
+ nsvc = tmp;
+ break;
+ }
+
+ return nsvc;
+}
+
+
+static struct gprs_ns2_vc *ns2_load_sharing(
+ struct gprs_ns2_nse *nse,
+ uint16_t bvci,
+ uint32_t link_selector)
+{
+ struct gprs_ns2_vc *nsvc = NULL;
+
+ if (bvci == 0) {
+ /* signalling */
+ nsvc = ns2_load_sharing_signal(nse);
+ } else {
+ /* data with load sharing parameter */
+ if (llist_empty(&nse->nsvc))
+ return NULL;
+
+ switch (nse->ll) {
+ case GPRS_NS2_LL_FR:
+ nsvc = ns2_load_sharing_modulor(nse, bvci, link_selector);
+ break;
+ default:
+ nsvc = ns2_load_sharing_first(nse);
+ break;
+ }
+ }
+
+ return nsvc;
+}
+
/*! Receive a primitive from the NS User (Gb).
* \param[in] nsi NS instance to which the primitive is issued
* \param[in] oph The primitive
* \return 0 on success; negative on error */
int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph)
{
- /* TODO: implement load distribution function */
/* TODO: implement resource distribution */
/* TODO: check for empty PDUs which can be sent to Request/Confirm
* the IP endpoint */
struct osmo_gprs_ns2_prim *nsp;
struct gprs_ns2_nse *nse = NULL;
- struct gprs_ns2_vc *nsvc = NULL, *tmp;
+ struct gprs_ns2_vc *nsvc = NULL;
uint16_t bvci, nsei;
uint8_t sducontrol = 0;
@@ -316,16 +447,7 @@ int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph)
if (!nse)
return -EINVAL;
- llist_for_each_entry(tmp, &nse->nsvc, list) {
- if (!gprs_ns2_vc_is_unblocked(tmp))
- continue;
- if (bvci == 0 && tmp->sig_weight == 0)
- continue;
- if (bvci != 0 && tmp->data_weight == 0)
- continue;
-
- nsvc = tmp;
- }
+ nsvc = ns2_load_sharing(nse, bvci, nsp->u.unitdata.link_selector);
/* TODO: send a status primitive back */
if (!nsvc)
@@ -345,9 +467,11 @@ int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph)
* \param[in] bvci BVCI to which the status relates
* \param[in] cause The cause of the status */
void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc *nsvc,
uint16_t bvci,
enum gprs_ns2_affecting_cause cause)
{
+ char nsvc_str[NS2_LL_MAX_STR];
struct osmo_gprs_ns2_prim nsp = {};
nsp.nsei = nse->nsei;
nsp.bvci = bvci;
@@ -355,6 +479,9 @@ void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
nsp.u.status.transfer = -1;
nsp.u.status.first = nse->first;
nsp.u.status.persistent = nse->persistent;
+ if (nsvc)
+ nsp.u.status.nsvc = gprs_ns2_ll_str_buf(nsvc_str, sizeof(nsvc_str), nsvc);
+
osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_STATUS,
PRIM_OP_INDICATION, NULL);
nse->nsi->cb(&nsp.oph, nse->nsi->cb_data);
@@ -412,7 +539,7 @@ void gprs_ns2_free_nsvc(struct gprs_ns2_vc *nsvc)
if (!nsvc)
return;
- ns2_prim_status_ind(nsvc->nse, 0, NS_AFF_CAUSE_VC_FAILURE);
+ ns2_prim_status_ind(nsvc->nse, nsvc, 0, NS_AFF_CAUSE_VC_FAILURE);
llist_del(&nsvc->list);
llist_del(&nsvc->blist);
@@ -463,7 +590,7 @@ static int reject_status_msg(struct msgb *orig_msg, struct tlv_parsed *tp, struc
if (!msg)
return -ENOMEM;
- if (TLVP_PRESENT(tp, NS_IE_NSEI)) {
+ if (TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
nsei = tlvp_val16be(tp, NS_IE_NSEI);
LOGP(DLNS, LOGL_NOTICE, "NSEI=%u Rejecting message without NSVCI. Tx NS STATUS (cause=%s)\n",
@@ -475,7 +602,7 @@ static int reject_status_msg(struct msgb *orig_msg, struct tlv_parsed *tp, struc
nsh->pdu_type = NS_PDUT_STATUS;
msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &_cause);
- have_vci = TLVP_PRESENT(tp, NS_IE_VCI);
+ have_vci = TLVP_PRES_LEN(tp, NS_IE_VCI, 2);
/* Section 9.2.7.1: Static conditions for NS-VCI */
if (cause == NS_CAUSE_NSVC_BLOCKED ||
@@ -545,7 +672,7 @@ struct gprs_ns2_vc *gprs_ns2_nsvc_by_nsvci(struct gprs_ns2_inst *nsi, uint16_t n
* \param[in] nsi NS instance in which to create NS Entity
* \param[in] nsei NS Entity Identifier of to-be-created NSE
* \returns newly-allocated NS-E in successful case; NULL on error */
-struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nsei)
+struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nsei, enum gprs_ns2_ll linklayer)
{
struct gprs_ns2_nse *nse;
@@ -559,6 +686,7 @@ struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nse
if (!nse)
return NULL;
+ nse->ll = linklayer;
nse->nsei = nsei;
nse->nsi = nsi;
nse->first = true;
@@ -590,7 +718,7 @@ void gprs_ns2_free_nse(struct gprs_ns2_nse *nse)
gprs_ns2_free_nsvc(nsvc);
}
- ns2_prim_status_ind(nse, 0, NS_AFF_CAUSE_FAILURE);
+ ns2_prim_status_ind(nse, NULL, 0, NS_AFF_CAUSE_FAILURE);
llist_del(&nse->list);
if (nse->bss_sns_fi)
@@ -645,42 +773,37 @@ enum gprs_ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
if (msg->len < sizeof(struct gprs_ns_hdr))
return GPRS_NS2_CS_ERROR;
- if (nsh->pdu_type == NS_PDUT_STATUS) {
+ switch (nsh->pdu_type) {
+ case NS_PDUT_STATUS:
/* Do not respond, see 3GPP TS 08.16, 7.5.1 */
LOGP(DLNS, LOGL_INFO, "Ignoring NS STATUS from %s "
"for non-existing NS-VC\n",
logname);
return GPRS_NS2_CS_SKIPPED;
- }
-
- if (nsh->pdu_type == NS_PDUT_ALIVE_ACK) {
+ case NS_PDUT_ALIVE_ACK:
/* Ignore this, see 3GPP TS 08.16, 7.4.1 */
LOGP(DLNS, LOGL_INFO, "Ignoring NS ALIVE ACK from %s "
"for non-existing NS-VC\n",
logname);
return GPRS_NS2_CS_SKIPPED;
- }
-
- if (nsh->pdu_type == NS_PDUT_RESET_ACK) {
+ case NS_PDUT_RESET_ACK:
/* Ignore this, see 3GPP TS 08.16, 7.3.1 */
LOGP(DLNS, LOGL_INFO, "Ignoring NS RESET ACK from %s "
"for non-existing NS-VC\n",
logname);
return GPRS_NS2_CS_SKIPPED;
- }
-
- if (bind->vc_mode == NS2_VC_MODE_BLOCKRESET) {
- /* Only the RESET procedure creates a new NSVC */
- if (nsh->pdu_type != NS_PDUT_RESET) {
- rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_PDU_INCOMP_PSTATE);
+ case NS_PDUT_RESET:
+ /* accept PDU RESET when vc_mode matches */
+ if (bind->vc_mode == NS2_VC_MODE_BLOCKRESET)
+ break;
- if (rc < 0) {
- LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
- return rc;
- }
- return GPRS_NS2_CS_REJECTED;
+ rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_PDU_INCOMP_PSTATE);
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
+ return rc;
}
- } else { /* NS2_VC_MODE_ALIVE */
+ return GPRS_NS2_CS_REJECTED;
+ default:
rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_PDU_INCOMP_PSTATE);
if (rc < 0) {
@@ -699,8 +822,8 @@ enum gprs_ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
return GPRS_NS2_CS_REJECTED;
}
- if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) ||
- !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) {
+ if (!TLVP_PRES_LEN(&tp, NS_IE_CAUSE, 1) ||
+ !TLVP_PRES_LEN(&tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(&tp, NS_IE_NSEI, 2)) {
LOGP(DLNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_MISSING_ESSENT_IE);
return GPRS_NS2_CS_REJECTED;
@@ -714,7 +837,7 @@ enum gprs_ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
return GPRS_NS2_CS_SKIPPED;
}
- nse = gprs_ns2_create_nse(bind->nsi, nsei);
+ nse = gprs_ns2_create_nse(bind->nsi, nsei, bind->ll);
if (!nse) {
return GPRS_NS2_CS_ERROR;
}
@@ -724,8 +847,6 @@ enum gprs_ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
if (!nsvc)
return GPRS_NS2_CS_SKIPPED;
- nsvc->ll = GPRS_NS_LL_UDP;
-
nsvci = tlvp_val16be(&tp, NS_IE_VCI);
nsvc->nsvci = nsvci;
nsvc->nsvci_is_valid = true;
@@ -795,7 +916,7 @@ struct gprs_ns2_vc *gprs_ns2_ip_connect2(struct gprs_ns2_vc_bind *bind,
struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
if (!nse) {
- nse = gprs_ns2_create_nse(bind->nsi, nsei);
+ nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_UDP);
if (!nse)
return NULL;
}
@@ -816,7 +937,7 @@ int gprs_ns2_ip_connect_sns(struct gprs_ns2_vc_bind *bind,
struct gprs_ns2_vc *nsvc;
if (!nse) {
- nse = gprs_ns2_create_nse(bind->nsi, nsei);
+ nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_UDP);
if (!nse)
return -1;
}
@@ -890,6 +1011,9 @@ int ns2_recv_vc(struct gprs_ns2_vc *nsvc,
struct tlv_parsed tp;
int rc = 0;
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
if (msg->len < sizeof(struct gprs_ns_hdr))
return -EINVAL;
@@ -954,6 +1078,20 @@ int ns2_recv_vc(struct gprs_ns2_vc *nsvc,
return rc;
}
+/* summarize all active data nsvcs */
+void ns2_nse_data_sum(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc;
+ nse->nsvc_data_count = 0;
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (!gprs_ns2_vc_is_unblocked(nsvc))
+ continue;
+ if (nsvc->data_weight > 0)
+ nse->nsvc_data_count++;
+ }
+}
+
/*! Notify a nse about the change of a NS-VC.
* \param[in] nsvc NS-VC which has detected the change (and shall not be notified).
* \param[in] unblocked whether the NSE should be marked as unblocked (true) or blocked (false) */
@@ -962,13 +1100,15 @@ void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked)
struct gprs_ns2_nse *nse = nsvc->nse;
struct gprs_ns2_vc *tmp;
+ ns2_nse_data_sum(nse);
+
if (unblocked == nse->alive)
return;
if (unblocked) {
/* this is the first unblocked NSVC on an unavailable NSE */
nse->alive = true;
- ns2_prim_status_ind(nse, 0, NS_AFF_CAUSE_RECOVERY);
+ ns2_prim_status_ind(nse, NULL, 0, NS_AFF_CAUSE_RECOVERY);
nse->first = false;
return;
}
@@ -986,12 +1126,12 @@ void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked)
/* nse became unavailable */
nse->alive = false;
- ns2_prim_status_ind(nse, 0, NS_AFF_CAUSE_FAILURE);
+ ns2_prim_status_ind(nse, NULL, 0, NS_AFF_CAUSE_FAILURE);
}
/*! Create a new GPRS NS instance
* \param[in] ctx a talloc context to allocate NS instance from
- * \param[in] cb Call-back function for dispatching primitives to the user
+ * \param[in] cb Call-back function for dispatching primitives to the user. The Call-back must free all msgb* given in the primitive.
* \param[in] cb_data transparent user data passed to Call-back
* \returns dynamically allocated gprs_ns_inst; NULL on error */
struct gprs_ns2_inst *gprs_ns2_instantiate(void *ctx, osmo_prim_cb cb, void *cb_data)
@@ -1061,7 +1201,7 @@ void gprs_ns2_start_alive_all_nsvcs(struct gprs_ns2_nse *nse)
/*! Set the mode of given bind.
* \param[in] bind the bind we want to set the mode of
- * \param[in] modde mode to set bind to */
+ * \param[in] mode mode to set bind to */
void gprs_ns2_bind_set_mode(struct gprs_ns2_vc_bind *bind, enum gprs_ns2_vc_mode mode)
{
bind->vc_mode = mode;
diff --git a/src/gb/gprs_ns2_fr.c b/src/gb/gprs_ns2_fr.c
new file mode 100644
index 00000000..a651e1d4
--- /dev/null
+++ b/src/gb/gprs_ns2_fr.c
@@ -0,0 +1,669 @@
+/*! \file gprs_ns2_fr.c
+ * NS-over-FR-over-GRE implementation.
+ * GPRS Networks Service (NS) messages on the Gb interface.
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * as well as its successor 3GPP TS 48.016 */
+
+/* (C) 2009-2010,2014,2017 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <linux/if_ether.h>
+#include <linux/hdlc.h>
+
+#include <osmocom/gprs/frame_relay.h>
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/mnl.h>
+#include <osmocom/gprs/gprs_ns2.h>
+
+#include "config.h"
+#include "common_vty.h"
+#include "gprs_ns2_internal.h"
+
+#define GRE_PTYPE_FR 0x6559
+#define GRE_PTYPE_IPv4 0x0800
+#define GRE_PTYPE_IPv6 0x86dd
+#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
+
+#ifndef IPPROTO_GRE
+# define IPPROTO_GRE 47
+#endif
+
+struct gre_hdr {
+ uint16_t flags;
+ uint16_t ptype;
+} __attribute__ ((packed));
+
+static void free_bind(struct gprs_ns2_vc_bind *bind);
+static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg);
+
+struct gprs_ns2_vc_driver vc_driver_fr = {
+ .name = "GB frame relay",
+ .free_bind = free_bind,
+};
+
+struct priv_bind {
+ struct osmo_fd fd;
+ char netif[IF_NAMESIZE];
+ struct osmo_fr_link *link;
+ bool if_running;
+};
+
+struct priv_vc {
+ struct osmo_sockaddr remote;
+ uint16_t dlci;
+ struct osmo_fr_dlc *dlc;
+};
+
+static void free_vc(struct gprs_ns2_vc *nsvc)
+{
+ OSMO_ASSERT(nsvc);
+
+ if (!nsvc->priv)
+ return;
+
+ talloc_free(nsvc->priv);
+ nsvc->priv = NULL;
+}
+
+static void dump_vty(const struct gprs_ns2_vc_bind *bind, struct vty *vty, bool _stats)
+{
+ struct priv_bind *priv;
+ struct gprs_ns2_vc *nsvc;
+ struct osmo_fr_link *fr_link;
+
+ if (!bind)
+ return;
+
+ priv = bind->priv;
+ fr_link = priv->link;
+
+ vty_out(vty, "FR bind: %s, role: %s, link: %s%s", priv->netif,
+ osmo_fr_role_str(fr_link->role), priv->if_running ? "UP" : "DOWN", VTY_NEWLINE);
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ vty_out(vty, " NSVCI %05u: %s%s", nsvc->nsvci, gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
+ }
+
+ priv = bind->priv;
+}
+
+/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
+static void free_bind(struct gprs_ns2_vc_bind *bind)
+{
+ struct priv_bind *priv;
+
+ if (!bind)
+ return;
+
+ priv = bind->priv;
+
+ OSMO_ASSERT(llist_empty(&bind->nsvc));
+
+ osmo_fr_link_free(priv->link);
+ osmo_fd_close(&priv->fd);
+ talloc_free(priv);
+}
+
+static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,
+ struct gprs_ns2_vc *nsvc,
+ uint16_t dlci)
+{
+ struct priv_bind *privb = bind->priv;
+ struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
+ if (!priv)
+ return NULL;
+
+ nsvc->priv = priv;
+ priv->dlci = dlci;
+ priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);
+ if (!priv->dlc) {
+ nsvc->priv = NULL;
+ talloc_free(priv);
+ return NULL;
+ }
+
+ priv->dlc->rx_cb_data = nsvc;
+ priv->dlc->rx_cb = fr_dlci_rx_cb;
+
+ return priv;
+}
+
+int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
+ uint16_t dlci,
+ struct gprs_ns2_vc **result)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct priv_vc *vcpriv;
+
+ if (!result)
+ return -EINVAL;
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ vcpriv = nsvc->priv;
+ if (vcpriv->dlci != dlci) {
+ *result = nsvc;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* PDU from the network interface towards the fr layer (upwards) */
+static int handle_netif_read(struct osmo_fd *bfd)
+{
+ struct gprs_ns2_vc_bind *bind = bfd->data;
+ struct priv_bind *priv = bind->priv;
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
+ int rc = 0;
+
+ if (!msg)
+ return -ENOMEM;
+
+ rc = read(bfd->fd, msg->data, NS_ALLOC_SIZE);
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
+ strerror(errno));
+ goto out_err;
+ } else if (rc == 0) {
+ goto out_err;
+ }
+
+ msgb_put(msg, rc);
+ msg->dst = priv->link;
+ return osmo_fr_rx(msg);
+
+out_err:
+ msgb_free(msg);
+ return rc;
+}
+
+/* PDU from the frame relay towards the NS-VC (upwards) */
+static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)
+{
+ int rc;
+ struct gprs_ns2_vc *nsvc = cb_data;
+
+ rc = ns2_recv_vc(nsvc, msg);
+
+ return rc;
+}
+
+static int handle_netif_write(struct osmo_fd *bfd)
+{
+ /* FIXME */
+ return -EIO;
+}
+
+static int fr_fd_cb(struct osmo_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & OSMO_FD_READ)
+ rc = handle_netif_read(bfd);
+ if (what & OSMO_FD_WRITE)
+ rc = handle_netif_write(bfd);
+
+ return rc;
+}
+
+/*! determine if given bind is for FR-GRE encapsulation. */
+int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
+{
+ return (bind->driver == &vc_driver_fr);
+}
+
+/* PDU from the NS-VC towards the frame relay layer (downwards) */
+static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
+{
+ struct priv_vc *vcpriv = nsvc->priv;
+
+ msg->dst = vcpriv->dlc;
+ return osmo_fr_tx_dlc(msg);
+}
+
+/* PDU from the frame relay layer towards the network interface (downwards) */
+int fr_tx_cb(void *data, struct msgb *msg)
+{
+ struct gprs_ns2_vc_bind *bind = data;
+ struct priv_bind *priv = bind->priv;
+ int rc;
+
+ /* FIXME half writes */
+ rc = write(priv->fd.fd, msg->data, msg->len);
+ msgb_free(msg);
+
+ return rc;
+}
+
+static int devname2ifindex(const char *ifname)
+{
+ struct ifreq ifr;
+ int sk, rc;
+
+ sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sk < 0)
+ return sk;
+
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0;
+
+ rc = ioctl(sk, SIOCGIFINDEX, &ifr);
+ close(sk);
+ if (rc < 0)
+ return rc;
+
+ return ifr.ifr_ifindex;
+}
+
+static int open_socket(const char *ifname)
+{
+ struct sockaddr_ll addr;
+ int ifindex;
+ int fd, rc, on = 1;
+
+ ifindex = devname2ifindex(ifname);
+ if (ifindex < 0) {
+ LOGP(DLNS, LOGL_ERROR, "Can not get interface index for interface %s\n", ifname);
+ return ifindex;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_protocol = htons(ETH_P_ALL);
+ addr.sll_ifindex = ifindex;
+
+ fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (fd < 0) {
+ LOGP(DLNS, LOGL_ERROR, "Can not get socket for interface %s. Are you root or have CAP_RAW_SOCKET?\n", ifname);
+ return fd;
+ }
+
+ if (ioctl(fd, FIONBIO, (unsigned char *)&on) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot set this socket unblocking: %s\n",
+ strerror(errno));
+ close(fd);
+ return -EINVAL;
+ }
+
+ rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_ERROR, "Can not bind for interface %s\n", ifname);
+ close(fd);
+ return rc;
+ }
+
+ return fd;
+}
+
+#ifdef ENABLE_LIBMNL
+
+#include <osmocom/core/mnl.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+#ifndef ARPHRD_FRAD
+#define ARPHRD_FRAD 770
+#endif
+
+/* validate the netlink attributes */
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ return MNL_CB_ERROR;
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+/* find the bind for the netdev (if any) */
+static struct gprs_ns2_vc_bind *bind4netdev(struct gprs_ns2_inst *nsi, const char *ifname)
+{
+ struct gprs_ns2_vc_bind *bind;
+
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ struct priv_bind *bpriv = bind->priv;
+ if (!strcmp(bpriv->netif, ifname))
+ return bind;
+ }
+
+ return NULL;
+}
+
+/* handle a single netlink message received via libmnl */
+static int linkmon_mnl_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct osmo_mnl *omnl = data;
+ struct gprs_ns2_vc_bind *bind;
+ struct nlattr *tb[IFLA_MAX+1] = {};
+ struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
+ struct gprs_ns2_inst *nsi;
+ const char *ifname;
+ bool if_running;
+
+ OSMO_ASSERT(omnl);
+ OSMO_ASSERT(ifm);
+
+ nsi = omnl->priv;
+
+ if (ifm->ifi_type != ARPHRD_FRAD)
+ return MNL_CB_OK;
+
+ mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
+
+ if (!tb[IFLA_IFNAME])
+ return MNL_CB_OK;
+ ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
+ if_running = !!(ifm->ifi_flags & IFF_RUNNING);
+
+ bind = bind4netdev(nsi, ifname);
+ if (bind) {
+ struct priv_bind *bpriv = bind->priv;
+ if (bpriv->if_running != if_running) {
+ /* update running state */
+ LOGP(DLNS, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
+ ifname, if_running ? "UP" : "DOWN");
+ bpriv->if_running = if_running;
+ }
+ }
+
+ return MNL_CB_OK;
+}
+
+/* trigger one initial dump of all link information */
+static void linkmon_initial_dump(struct osmo_mnl *omnl)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct rtgenmsg *rt;
+
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = time(NULL);
+ rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+ rt->rtgen_family = AF_PACKET;
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLNS, LOGL_ERROR, "linkmon: Cannot send rtnetlink message: %s\n", strerror(errno));
+ }
+
+ /* the response[s] will be handled just like the events */
+}
+#endif /* LIBMNL */
+
+
+/*! Create a new bind for NS over FR.
+ * \param[in] nsi NS instance in which to create the bind
+ * \param[in] netif Network interface to bind to
+ * \param[in] fr_network
+ * \param[in] fr_role
+ * \param[out] result pointer to created bind
+ * \return 0 on success; negative on error */
+int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
+ const char *netif,
+ struct osmo_fr_network *fr_network,
+ enum osmo_fr_role fr_role,
+ struct gprs_ns2_vc_bind **result)
+{
+ struct gprs_ns2_vc_bind *bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
+ struct priv_bind *priv;
+ struct osmo_fr_link *fr_link;
+ int rc = 0;
+
+ if (!bind)
+ return -ENOSPC;
+
+ bind->driver = &vc_driver_fr;
+ bind->ll = GPRS_NS2_LL_FR;
+ bind->send_vc = fr_vc_sendmsg;
+ bind->free_vc = free_vc;
+ bind->dump_vty = dump_vty;
+ bind->nsi = nsi;
+ priv = bind->priv = talloc_zero(bind, struct priv_bind);
+ if (!priv) {
+ rc = -ENOSPC;
+ goto err_bind;
+ }
+
+ priv->fd.cb = fr_fd_cb;
+ priv->fd.data = bind;
+ if (strlen(netif) > IF_NAMESIZE) {
+ rc = -EINVAL;
+ goto err_priv;
+ }
+ strncpy(priv->netif, netif, sizeof(priv->netif));
+
+ ns2_vty_bind_apply(bind);
+ if (result)
+ *result = bind;
+
+ /* FIXME: move fd handling into socket.c */
+ fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);
+ if (!fr_link) {
+ rc = -EINVAL;
+ goto err_priv;
+ }
+
+ fr_link->tx_cb = fr_tx_cb;
+ fr_link->tx_cb_data = bind;
+ priv->link = fr_link;
+ priv->fd.fd = rc = open_socket(netif);
+ if (rc < 0)
+ goto err_fr;
+
+ priv->fd.when = OSMO_FD_READ;
+ rc = osmo_fd_register(&priv->fd);
+ if (rc < 0)
+ goto err_fd;
+
+ INIT_LLIST_HEAD(&bind->nsvc);
+ llist_add(&bind->list, &nsi->binding);
+
+#ifdef ENABLE_LIBMNL
+ if (!nsi->linkmon_mnl)
+ nsi->linkmon_mnl = osmo_mnl_init(nsi, NETLINK_ROUTE, RTMGRP_LINK, linkmon_mnl_cb, nsi);
+
+ /* we get a new full dump after every bind. which is a bit excessive. But that's just once
+ * at start-up, so we can get away with it */
+ if (nsi->linkmon_mnl)
+ linkmon_initial_dump(nsi->linkmon_mnl);
+#endif
+
+ return rc;
+
+err_fd:
+ close(priv->fd.fd);
+err_fr:
+ osmo_fr_link_free(fr_link);
+err_priv:
+ talloc_free(priv);
+err_bind:
+ talloc_free(bind);
+
+ return rc;
+}
+
+/*! Return the network interface of the bind
+ * \param[in] bind The bind
+ * \return the network interface
+ */
+const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)
+{
+ struct priv_bind *priv;
+
+ if (bind->driver != &vc_driver_fr)
+ return NULL;
+
+ priv = bind->priv;
+ return priv->netif;
+}
+
+/*! Find NS bind for a given network interface
+ * \param[in] nsi NS instance
+ * \param[in] netif the network interface to search for
+ * \return the bind or NULL if not found
+ */
+struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
+ struct gprs_ns2_inst *nsi,
+ const char *netif)
+{
+ struct gprs_ns2_vc_bind *bind;
+ const char *_netif;
+
+ OSMO_ASSERT(nsi);
+ OSMO_ASSERT(netif);
+
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ if (!gprs_ns2_is_fr_bind(bind))
+ continue;
+
+ _netif = gprs_ns2_fr_bind_netif(bind);
+ if (!strncmp(_netif, netif, IF_NAMESIZE))
+ return bind;
+ }
+
+ return NULL;
+}
+
+/*! Create, connect and activate a new FR-based NS-VC
+ * \param[in] bind bind in which the new NS-VC is to be created
+ * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
+ * \param[in] dlci Data Link connection identifier
+ * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
+struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
+ uint16_t nsei,
+ uint16_t nsvci,
+ uint16_t dlci)
+{
+ bool created_nse = false;
+ struct gprs_ns2_vc *nsvc = NULL;
+ struct priv_vc *priv = NULL;
+ struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
+ if (!nse) {
+ nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_FR);
+ if (!nse)
+ return NULL;
+ created_nse = true;
+ }
+
+ nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
+ if (nsvc) {
+ goto err_nse;
+ }
+
+ nsvc = ns2_vc_alloc(bind, nse, true);
+ if (!nsvc)
+ goto err_nse;
+
+ nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
+ if (!priv)
+ goto err;
+
+ nsvc->nsvci = nsvci;
+ nsvc->nsvci_is_valid = true;
+
+ gprs_ns2_vc_fsm_start(nsvc);
+
+ return nsvc;
+
+err:
+ gprs_ns2_free_nsvc(nsvc);
+err_nse:
+ if (created_nse)
+ gprs_ns2_free_nse(nse);
+
+ return NULL;
+}
+
+/*! Return the nsvc by dlci.
+ * \param[in] bind
+ * \param[in] dlci Data Link connection identifier
+ * \return the nsvc or NULL if not found
+ */
+struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,
+ uint16_t dlci)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct priv_vc *vcpriv;
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ vcpriv = nsvc->priv;
+
+ if (dlci == vcpriv->dlci)
+ return nsvc;
+ }
+
+ return NULL;
+}
+
+/*! Return the dlci of the nsvc
+ * \param[in] nsvc
+ * \return the dlci or 0 on error. 0 is not a valid dlci.
+ */
+uint16_t gprs_ns2_fr_nsvc_dlci(struct gprs_ns2_vc *nsvc)
+{
+ struct priv_vc *vcpriv;
+
+ if (!nsvc->bind)
+ return 0;
+
+ if (nsvc->bind->driver != &vc_driver_fr)
+ return 0;
+
+ vcpriv = nsvc->priv;
+ return vcpriv->dlci;
+}
diff --git a/src/gb/gprs_ns2_frgre.c b/src/gb/gprs_ns2_frgre.c
index cd478d6e..3c276bc5 100644
--- a/src/gb/gprs_ns2_frgre.c
+++ b/src/gb/gprs_ns2_frgre.c
@@ -555,6 +555,7 @@ int gprs_ns2_frgre_bind(struct gprs_ns2_inst *nsi,
}
bind->driver = &vc_driver_frgre;
+ bind->ll = GPRS_NS2_LL_FR_GRE;
bind->send_vc = frgre_vc_sendmsg;
bind->free_vc = free_vc;
bind->nsi = nsi;
diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h
index b480391c..e72deff1 100644
--- a/src/gb/gprs_ns2_internal.h
+++ b/src/gb/gprs_ns2_internal.h
@@ -56,13 +56,6 @@ enum ns_stat {
NS_STAT_ALIVE_DELAY,
};
-/*! Osmocom NS link layer types */
-enum gprs_ns_ll {
- GPRS_NS_LL_UDP, /*!< NS/UDP/IP */
- GPRS_NS_LL_E1, /*!< NS/E1 */
- GPRS_NS_LL_FR_GRE, /*!< NS/FR/GRE/IP */
-};
-
/*! Osmocom NS2 VC create status */
enum gprs_ns2_cs {
GPRS_NS2_CS_CREATED, /*!< A NSVC object has been created */
@@ -102,8 +95,12 @@ struct gprs_ns2_inst {
/*! workaround for rate counter until rate counter accepts char str as index */
uint32_t rate_ctr_idx;
+
+ /*! libmnl netlink socket for link state monitoring */
+ struct osmo_mnl *linkmon_mnl;
};
+
/*! Structure repesenting a NSE. The BSS/PCU will only have a single NSE, while SGSN has one for each BSS/PCU */
struct gprs_ns2_nse {
uint16_t nsei;
@@ -117,6 +114,9 @@ struct gprs_ns2_nse {
/*! llist head to hold all nsvc */
struct llist_head nsvc;
+ /*! count all active NSVCs with data capabilities */
+ int nsvc_data_count;
+
/*! true if this NSE was created by VTY or pcu socket) */
bool persistent;
@@ -127,6 +127,9 @@ struct gprs_ns2_nse {
/*! true if this NSE has at least one alive VC */
bool alive;
+ /*! which link-layer are we based on? */
+ enum gprs_ns2_ll ll;
+
struct osmo_fsm_inst *bss_sns_fi;
};
@@ -153,7 +156,10 @@ struct gprs_ns2_vc {
/*! signalling weight. 0 = don't use for signalling (BVCI == 0)*/
uint8_t sig_weight;
- /*! signaling weight. 0 = don't use for user data (BVCI != 0) */
+ /*! signalling packet counter for the load sharing function */
+ uint8_t sig_counter;
+
+ /*! data weight. 0 = don't use for user data (BVCI != 0) */
uint8_t data_weight;
/*! can be used by the bind/driver of the virtual circuit. e.g. ipv4/ipv6/frgre/e1 */
@@ -165,8 +171,6 @@ struct gprs_ns2_vc {
struct rate_ctr_group *ctrg;
struct osmo_stat_item_group *statg;
- /*! which link-layer are we based on? */
- enum gprs_ns_ll ll;
enum gprs_ns2_vc_mode mode;
struct osmo_fsm_inst *fi;
@@ -187,6 +191,9 @@ struct gprs_ns2_vc_bind {
/*! if VCs use reset/block/unblock method. IP shall not use this */
enum gprs_ns2_vc_mode vc_mode;
+ /*! which link-layer are we based on? */
+ enum gprs_ns2_ll ll;
+
/*! send a msg over a VC */
int (*send_vc)(struct gprs_ns2_vc *nsvc, struct msgb *msg);
@@ -221,6 +228,7 @@ struct msgb *gprs_ns2_msgb_alloc(void);
void gprs_ns2_sns_dump_vty(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats);
void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc *nsvc,
uint16_t bvci,
enum gprs_ns2_affecting_cause cause);
void ns2_nse_notify_alive(struct gprs_ns2_vc *nsvc, bool alive);
@@ -285,6 +293,7 @@ void ns2_sns_free_nsvc(struct gprs_ns2_vc *nsvc);
struct osmo_fsm_inst *gprs_ns2_vc_fsm_alloc(struct gprs_ns2_vc *nsvc,
const char *id, bool initiate);
int gprs_ns2_vc_fsm_start(struct gprs_ns2_vc *nsvc);
+int gprs_ns2_vc_force_unconfigured(struct gprs_ns2_vc *nsvc);
int gprs_ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp);
int gprs_ns2_vc_is_alive(struct gprs_ns2_vc *nsvc);
int gprs_ns2_vc_is_unblocked(struct gprs_ns2_vc *nsvc);
diff --git a/src/gb/gprs_ns2_message.c b/src/gb/gprs_ns2_message.c
index fac6108c..eb9a1984 100644
--- a/src/gb/gprs_ns2_message.c
+++ b/src/gb/gprs_ns2_message.c
@@ -66,7 +66,8 @@ enum ns_ctr {
static int gprs_ns2_validate_reset(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
{
- if (!TLVP_PRESENT(tp, NS_IE_CAUSE) || !TLVP_PRESENT(tp, NS_IE_VCI) || !TLVP_PRESENT(tp, NS_IE_NSEI)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1) ||
+ !TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
@@ -76,7 +77,7 @@ static int gprs_ns2_validate_reset(struct gprs_ns2_vc *nsvc, struct msgb *msg, s
static int gprs_ns2_validate_reset_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
{
- if (!TLVP_PRESENT(tp, NS_IE_VCI) || !TLVP_PRESENT(tp, NS_IE_NSEI)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
@@ -86,7 +87,7 @@ static int gprs_ns2_validate_reset_ack(struct gprs_ns2_vc *nsvc, struct msgb *ms
static int gprs_ns2_validate_block(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
{
- if (!TLVP_PRESENT(tp, NS_IE_VCI) || !TLVP_PRESENT(tp, NS_IE_CAUSE)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
@@ -96,7 +97,7 @@ static int gprs_ns2_validate_block(struct gprs_ns2_vc *nsvc, struct msgb *msg, s
static int gprs_ns2_validate_block_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
{
- if (!TLVP_PRESENT(tp, NS_IE_VCI)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
@@ -107,7 +108,7 @@ static int gprs_ns2_validate_block_ack(struct gprs_ns2_vc *nsvc, struct msgb *ms
static int gprs_ns2_validate_status(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
{
- if (!TLVP_PRESENT(tp, NS_IE_CAUSE)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
@@ -117,7 +118,7 @@ static int gprs_ns2_validate_status(struct gprs_ns2_vc *nsvc, struct msgb *msg,
switch (_cause) {
case NS_CAUSE_NSVC_BLOCKED:
case NS_CAUSE_NSVC_UNKNOWN:
- if (!TLVP_PRESENT(tp, NS_IE_CAUSE)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
@@ -127,19 +128,19 @@ static int gprs_ns2_validate_status(struct gprs_ns2_vc *nsvc, struct msgb *msg,
case NS_CAUSE_PROTO_ERR_UNSPEC:
case NS_CAUSE_INVAL_ESSENT_IE:
case NS_CAUSE_MISSING_ESSENT_IE:
- if (!TLVP_PRESENT(tp, NS_IE_CAUSE)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
break;
case NS_CAUSE_BVCI_UNKNOWN:
- if (!TLVP_PRESENT(tp, NS_IE_BVCI)) {
+ if (!TLVP_PRES_LEN(tp, NS_IE_BVCI, 2)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
break;
case NS_CAUSE_UNKN_IP_TEST_FAILED:
- if (!TLVP_PRESENT (tp, NS_IE_IPv4_LIST) && !TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
+ if (!TLVP_PRESENT(tp, NS_IE_IPv4_LIST) && !TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
*cause = NS_CAUSE_MISSING_ESSENT_IE;
return -1;
}
@@ -189,6 +190,7 @@ static int ns2_tx_simple(struct gprs_ns2_vc *nsvc, uint8_t pdu_type)
struct msgb *msg = gprs_ns2_msgb_alloc();
struct gprs_ns_hdr *nsh;
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
if (!msg)
@@ -212,6 +214,7 @@ int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause)
struct gprs_ns_hdr *nsh;
uint16_t nsvci = osmo_htons(nsvc->nsvci);
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK");
@@ -244,6 +247,7 @@ int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc)
struct gprs_ns_hdr *nsh;
uint16_t nsvci = osmo_htons(nsvc->nsvci);
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK ACK");
@@ -275,6 +279,7 @@ int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
uint16_t nsvci = osmo_htons(nsvc->nsvci);
uint16_t nsei = osmo_htons(nsvc->nse->nsei);
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
@@ -307,6 +312,7 @@ int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc)
uint16_t nsvci, nsei;
/* Section 9.2.6 */
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
@@ -337,6 +343,7 @@ int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc)
* \returns 0 in case of success */
int ns2_tx_unblock(struct gprs_ns2_vc *nsvc)
{
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK");
@@ -353,6 +360,7 @@ int ns2_tx_unblock(struct gprs_ns2_vc *nsvc)
* \returns 0 in case of success */
int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc)
{
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK ACK");
@@ -368,6 +376,7 @@ int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc)
* \returns 0 in case of success */
int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
{
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n",
nsvc->nse->nsei, nsvc->nsvci);
@@ -380,6 +389,7 @@ int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
* \returns 0 in case of success */
int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc)
{
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
LOGP(DLNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n",
nsvc->nse->nsei, nsvc->nsvci);
@@ -399,6 +409,7 @@ int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
{
struct gprs_ns_hdr *nsh;
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
@@ -430,6 +441,7 @@ int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
struct gprs_ns_hdr *nsh;
uint16_t nsvci = osmo_htons(nsvc->nsvci);
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
bvci = osmo_htons(bvci);
@@ -495,6 +507,7 @@ int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
msg = gprs_ns2_msgb_alloc();
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
if (!msg)
return -ENOMEM;
@@ -553,6 +566,7 @@ int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
msg = gprs_ns2_msgb_alloc();
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
if (!msg)
return -ENOMEM;
@@ -601,6 +615,7 @@ int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
return -1;
msg = gprs_ns2_msgb_alloc();
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
if (!msg)
return -ENOMEM;
@@ -646,6 +661,7 @@ int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_n
msg = gprs_ns2_msgb_alloc();
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
if (!msg)
return -ENOMEM;
@@ -685,6 +701,7 @@ int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
struct gprs_ns_hdr *nsh;
uint16_t nsei;
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
if (!msg)
return -ENOMEM;
diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c
index 1afd4b7c..31f8a5f5 100644
--- a/src/gb/gprs_ns2_sns.c
+++ b/src/gb/gprs_ns2_sns.c
@@ -91,6 +91,7 @@ static const struct value_string gprs_sns_event_names[] = {
{ GPRS_SNS_EV_ADD, "ADD" },
{ GPRS_SNS_EV_DELETE, "DELETE" },
{ GPRS_SNS_EV_CHANGE_WEIGHT, "CHANGE_WEIGHT" },
+ { GPRS_SNS_EV_NO_NSVC, "NO_NSVC" },
{ 0, NULL }
};
@@ -232,7 +233,7 @@ void ns2_sns_free_nsvc(struct gprs_ns2_vc *nsvc)
gss->sns_nsvc = tmp;
}
} else {
- LOGPFSML(fi, LOGL_ERROR, "NSE %d: no remaining NSVC. Reseting SNS FSM.", nse->nsei);
+ LOGPFSML(fi, LOGL_ERROR, "NSE %d: no remaining NSVC, resetting SNS FSM\n", nse->nsei);
gss->sns_nsvc = NULL;
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_NO_NSVC, NULL);
}
@@ -685,7 +686,7 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
if (old_state != GPRS_SNS_ST_UNCONFIGURED)
- ns2_prim_status_ind(gss->nse, 0, NS_AFF_CAUSE_SNS_FAILURE);
+ ns2_prim_status_ind(gss->nse, NULL, 0, NS_AFF_CAUSE_SNS_FAILURE);
if (gss->num_max_ip4_remote > 0)
ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, gss->num_max_ip4_remote, -1);
@@ -1125,7 +1126,7 @@ static void ns2_sns_st_configured(struct osmo_fsm_inst *fi, uint32_t event, void
static void ns2_sns_st_configured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
{
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
- ns2_prim_status_ind(nse, 0, NS_AFF_CAUSE_SNS_CONFIGURED);
+ ns2_prim_status_ind(nse, NULL, 0, NS_AFF_CAUSE_SNS_CONFIGURED);
}
static const struct osmo_fsm_state ns2_sns_bss_states[] = {
diff --git a/src/gb/gprs_ns2_udp.c b/src/gb/gprs_ns2_udp.c
index 2cc10064..928116d6 100644
--- a/src/gb/gprs_ns2_udp.c
+++ b/src/gb/gprs_ns2_udp.c
@@ -97,11 +97,11 @@ static void dump_vty(const struct gprs_ns2_vc_bind *bind,
nsvcs++;
}
- vty_out(vty, "UDP bind: %s:%d dcsp: %d%s", sockstr.ip, sockstr.port, priv->dscp, VTY_NEWLINE);
+ vty_out(vty, "UDP bind: %s:%d DSCP: %d%s", sockstr.ip, sockstr.port, priv->dscp, VTY_NEWLINE);
vty_out(vty, " %lu NS-VC: %s", nsvcs, VTY_NEWLINE);
llist_for_each_entry(nsvc, &bind->nsvc, blist) {
- vty_out(vty, " %s%s", gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
+ vty_out(vty, " NSVCI %05u: %s%s", nsvc->nsvci, gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
}
}
@@ -206,7 +206,7 @@ static struct priv_vc *ns2_driver_alloc_vc(struct gprs_ns2_vc_bind *bind, struct
static int handle_nsip_read(struct osmo_fd *bfd)
{
- int rc;
+ int rc = 0;
int error = 0;
struct gprs_ns2_vc_bind *bind = bfd->data;
struct osmo_sockaddr saddr;
@@ -240,10 +240,10 @@ static int handle_nsip_read(struct osmo_fd *bfd)
}
}
- rc = ns2_recv_vc(nsvc, msg);
+ return ns2_recv_vc(nsvc, msg);
+
out:
msgb_free(msg);
-
return rc;
}
@@ -322,6 +322,7 @@ int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
}
bind->driver = &vc_driver_ip;
+ bind->ll = GPRS_NS2_LL_UDP;
bind->send_vc = nsip_vc_sendmsg;
bind->free_vc = free_vc;
bind->dump_vty = dump_vty;
@@ -379,6 +380,9 @@ struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
struct priv_vc *priv;
nsvc = ns2_vc_alloc(bind, nse, true);
+ if (!nsvc)
+ return NULL;
+
nsvc->priv = talloc_zero(bind, struct priv_vc);
if (!nsvc->priv) {
gprs_ns2_free_nsvc(nsvc);
@@ -388,8 +392,6 @@ struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
priv = nsvc->priv;
priv->remote = *remote;
- nsvc->ll = GPRS_NS_LL_UDP;
-
return nsvc;
}
@@ -400,9 +402,6 @@ const struct osmo_sockaddr *gprs_ns2_ip_vc_local(const struct gprs_ns2_vc *nsvc)
{
struct priv_bind *priv;
- if (nsvc->ll != GPRS_NS_LL_UDP)
- return NULL;
-
if (nsvc->bind->driver != &vc_driver_ip)
return NULL;
@@ -417,7 +416,7 @@ const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc *nsvc
{
struct priv_vc *priv;
- if (nsvc->ll != GPRS_NS_LL_UDP)
+ if (nsvc->bind->driver != &vc_driver_ip)
return NULL;
priv = nsvc->priv;
@@ -439,7 +438,7 @@ bool gprs_ns2_ip_vc_equal(const struct gprs_ns2_vc *nsvc,
struct priv_vc *vpriv;
struct priv_bind *bpriv;
- if (nsvc->ll != GPRS_NS_LL_UDP)
+ if (nsvc->bind->driver != &vc_driver_ip)
return false;
vpriv = nsvc->priv;
diff --git a/src/gb/gprs_ns2_vc_fsm.c b/src/gb/gprs_ns2_vc_fsm.c
index d13f1ce6..08b6b2d0 100644
--- a/src/gb/gprs_ns2_vc_fsm.c
+++ b/src/gb/gprs_ns2_vc_fsm.c
@@ -55,9 +55,10 @@ struct gprs_ns2_vc_priv {
struct gprs_ns2_vc *nsvc;
/* how often the timer was triggered */
int N;
- /* The initiater is responsible to UNBLOCK the VC. The BSS is usually the initiater.
- * It can change while runtime. The side which blocks an unblocked side.*/
- bool initiater;
+ /* The initiator is responsible to UNBLOCK the VC. The BSS is usually the initiator.
+ * It can change during runtime. The side which blocks an unblocked side.*/
+ bool initiate_block;
+ bool initiate_reset;
/* the alive counter is present in all states */
struct {
@@ -109,20 +110,23 @@ enum gprs_ns2_vc_event {
GPRS_NS2_EV_STATUS,
GPRS_NS2_EV_UNITDATA,
+
+ GPRS_NS2_EV_FORCE_UNCONFIGURED,
};
static const struct value_string gprs_ns2_vc_event_names[] = {
- { GPRS_NS2_EV_START, "START" },
- { GPRS_NS2_EV_RESET, "RESET" },
- { GPRS_NS2_EV_RESET_ACK, "RESET_ACK" },
- { GPRS_NS2_EV_UNBLOCK, "UNBLOCK" },
- { GPRS_NS2_EV_UNBLOCK_ACK, "UNBLOCK_ACK" },
- { GPRS_NS2_EV_BLOCK, "BLOCK" },
- { GPRS_NS2_EV_BLOCK_ACK, "BLOCK_ACK" },
- { GPRS_NS2_EV_ALIVE, "ALIVE" },
- { GPRS_NS2_EV_ALIVE_ACK, "ALIVE_ACK" },
- { GPRS_NS2_EV_STATUS, "STATUS" },
- { GPRS_NS2_EV_UNITDATA, "UNITDATA" },
+ { GPRS_NS2_EV_START, "START" },
+ { GPRS_NS2_EV_RESET, "RESET" },
+ { GPRS_NS2_EV_RESET_ACK, "RESET_ACK" },
+ { GPRS_NS2_EV_UNBLOCK, "UNBLOCK" },
+ { GPRS_NS2_EV_UNBLOCK_ACK, "UNBLOCK_ACK" },
+ { GPRS_NS2_EV_BLOCK, "BLOCK" },
+ { GPRS_NS2_EV_BLOCK_ACK, "BLOCK_ACK" },
+ { GPRS_NS2_EV_ALIVE, "ALIVE" },
+ { GPRS_NS2_EV_ALIVE_ACK, "ALIVE_ACK" },
+ { GPRS_NS2_EV_STATUS, "STATUS" },
+ { GPRS_NS2_EV_UNITDATA, "UNITDATA" },
+ {GPRS_NS2_EV_FORCE_UNCONFIGURED, "FORCE_UNCONFIGURED"},
{ 0, NULL }
};
@@ -241,7 +245,7 @@ static void gprs_ns2_st_reset_onenter(struct osmo_fsm_inst *fi, uint32_t old_sta
if (old_state != GPRS_NS2_ST_RESET)
priv->N = 0;
- if (priv->initiater)
+ if (priv->initiate_reset)
ns2_tx_reset(priv->nsvc, NS_CAUSE_OM_INTERVENTION);
stop_test_procedure(priv);
@@ -253,7 +257,7 @@ static void gprs_ns2_st_reset(struct osmo_fsm_inst *fi, uint32_t event, void *da
struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
struct gprs_ns2_vc_priv *priv = fi->priv;
- if (priv->initiater) {
+ if (priv->initiate_reset) {
switch (event) {
case GPRS_NS2_EV_RESET_ACK:
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED,
@@ -279,7 +283,7 @@ static void gprs_ns2_st_blocked_onenter(struct osmo_fsm_inst *fi, uint32_t old_s
if (old_state != GPRS_NS2_ST_BLOCKED)
priv->N = 0;
- if (priv->initiater)
+ if (priv->initiate_block)
ns2_tx_unblock(priv->nsvc);
start_test_procedure(priv);
@@ -289,7 +293,7 @@ static void gprs_ns2_st_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *
{
struct gprs_ns2_vc_priv *priv = fi->priv;
- if (priv->initiater) {
+ if (priv->initiate_block) {
switch (event) {
case GPRS_NS2_EV_BLOCK:
/* TODO: BLOCK is a UNBLOCK_NACK */
@@ -318,8 +322,11 @@ static void gprs_ns2_st_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *
static void gprs_ns2_st_unblocked_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state)
{
struct gprs_ns2_vc_priv *priv = fi->priv;
+ struct gprs_ns2_vc *nsvc = priv->nsvc;
+ struct gprs_ns2_nse *nse = nsvc->nse;
- ns2_nse_notify_unblocked(priv->nsvc, true);
+ ns2_nse_notify_unblocked(nsvc, true);
+ ns2_prim_status_ind(nse, nsvc, 0, NS_AFF_CAUSE_VC_RECOVERY);
}
static void gprs_ns2_st_unblocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -328,7 +335,7 @@ static void gprs_ns2_st_unblocked(struct osmo_fsm_inst *fi, uint32_t event, void
switch (event) {
case GPRS_NS2_EV_BLOCK:
- priv->initiater = false;
+ priv->initiate_block = false;
ns2_tx_block_ack(priv->nsvc);
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED,
0, 2);
@@ -375,7 +382,8 @@ static const struct osmo_fsm_state gprs_ns2_vc_states[] = {
[GPRS_NS2_ST_RESET] = {
.in_event_mask = S(GPRS_NS2_EV_RESET_ACK) | S(GPRS_NS2_EV_RESET),
.out_state_mask = S(GPRS_NS2_ST_RESET) |
- S(GPRS_NS2_ST_BLOCKED),
+ S(GPRS_NS2_ST_BLOCKED) |
+ S(GPRS_NS2_ST_UNCONFIGURED),
.name = "RESET",
.action = gprs_ns2_st_reset,
.onenter = gprs_ns2_st_reset_onenter,
@@ -385,7 +393,8 @@ static const struct osmo_fsm_state gprs_ns2_vc_states[] = {
S(GPRS_NS2_EV_UNBLOCK) | S(GPRS_NS2_EV_UNBLOCK_ACK),
.out_state_mask = S(GPRS_NS2_ST_RESET) |
S(GPRS_NS2_ST_UNBLOCKED) |
- S(GPRS_NS2_ST_BLOCKED),
+ S(GPRS_NS2_ST_BLOCKED) |
+ S(GPRS_NS2_ST_UNCONFIGURED),
.name = "BLOCKED",
.action = gprs_ns2_st_blocked,
.onenter = gprs_ns2_st_blocked_onenter,
@@ -393,7 +402,8 @@ static const struct osmo_fsm_state gprs_ns2_vc_states[] = {
[GPRS_NS2_ST_UNBLOCKED] = {
.in_event_mask = S(GPRS_NS2_EV_BLOCK),
.out_state_mask = S(GPRS_NS2_ST_RESET) | S(GPRS_NS2_ST_ALIVE) |
- S(GPRS_NS2_ST_BLOCKED),
+ S(GPRS_NS2_ST_BLOCKED) |
+ S(GPRS_NS2_ST_UNCONFIGURED),
.name = "UNBLOCKED",
.action = gprs_ns2_st_unblocked,
.onenter = gprs_ns2_st_unblocked_on_enter,
@@ -403,7 +413,8 @@ static const struct osmo_fsm_state gprs_ns2_vc_states[] = {
[GPRS_NS2_ST_ALIVE] = {
.in_event_mask = S(GPRS_NS2_EV_ALIVE_ACK),
.out_state_mask = S(GPRS_NS2_ST_RESET) |
- S(GPRS_NS2_ST_UNBLOCKED),
+ S(GPRS_NS2_ST_UNBLOCKED) |
+ S(GPRS_NS2_ST_UNCONFIGURED),
.name = "ALIVE",
.action = gprs_ns2_st_alive,
.onenter = gprs_ns2_st_alive_onenter,
@@ -416,10 +427,10 @@ static int gprs_ns2_vc_fsm_timer_cb(struct osmo_fsm_inst *fi)
struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
struct gprs_ns2_vc_priv *priv = fi->priv;
- if (priv->initiater) {
- /* PCU timeouts */
- switch (fi->state) {
- case GPRS_NS2_ST_RESET:
+ /* PCU timeouts */
+ switch (fi->state) {
+ case GPRS_NS2_ST_RESET:
+ if (priv->initiate_reset) {
priv->N++;
if (priv->N <= nsi->timeout[NS_TOUT_TNS_RESET_RETRIES]) {
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
@@ -427,16 +438,20 @@ static int gprs_ns2_vc_fsm_timer_cb(struct osmo_fsm_inst *fi)
priv->N = 0;
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
}
- break;
- case GPRS_NS2_ST_BLOCKED:
+ }
+ break;
+ case GPRS_NS2_ST_BLOCKED:
+ if (priv->initiate_block) {
priv->N++;
if (priv->N <= nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES]) {
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
} else {
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
}
- break;
- case GPRS_NS2_ST_ALIVE:
+ }
+ break;
+ case GPRS_NS2_ST_ALIVE:
+ if (priv->initiate_reset) {
priv->N++;
if (priv->N <= nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_ALIVE, 0, 0);
@@ -446,6 +461,7 @@ static int gprs_ns2_vc_fsm_timer_cb(struct osmo_fsm_inst *fi)
}
break;
}
+ break;
}
return 0;
}
@@ -459,8 +475,10 @@ static void gprs_ns2_recv_unitdata(struct osmo_fsm_inst *fi,
struct osmo_gprs_ns2_prim nsp = {};
uint16_t bvci;
- if (msgb_l2len(msg) < sizeof(*nsh) + 3)
+ if (msgb_l2len(msg) < sizeof(*nsh) + 3) {
+ msgb_free(msg);
return;
+ }
/* TODO: 7.1: For an IP sub-network, an NS-UNITDATA PDU
* for a PTP BVC may indicate a request to change the IP endpoint
@@ -488,6 +506,7 @@ static void gprs_ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
{
struct gprs_ns2_vc_priv *priv = fi->priv;
struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+ struct msgb *msg = data;
switch (event) {
case GPRS_NS2_EV_RESET:
@@ -496,7 +515,7 @@ static void gprs_ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
/* move the FSM into reset */
if (fi->state != GPRS_NS2_ST_RESET) {
- priv->initiater = false;
+ priv->initiate_reset = false;
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], NS_TOUT_TNS_RESET);
}
/* pass the event down into FSM action */
@@ -520,36 +539,60 @@ static void gprs_ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
recv_test_procedure(fi);
break;
case GPRS_NS2_EV_UNITDATA:
+ /* UNITDATA has to handle the release of msg.
+ * If send upwards (gprs_ns2_recv_unitdata) it must NOT free
+ * the msg, the upper layer has to do it.
+ * Otherwise the msg must be freed.
+ */
switch (fi->state) {
case GPRS_NS2_ST_BLOCKED:
/* 7.2.1: the BLOCKED_ACK might be lost */
- if (priv->initiater)
- gprs_ns2_recv_unitdata(fi, data);
- else
- ns2_tx_status(priv->nsvc,
- NS_CAUSE_NSVC_BLOCKED,
- 0, data);
+ if (priv->initiate_block) {
+ gprs_ns2_recv_unitdata(fi, msg);
+ return;
+ }
+
+ ns2_tx_status(priv->nsvc,
+ NS_CAUSE_NSVC_BLOCKED,
+ 0, msg);
break;
/* ALIVE can receive UNITDATA if the ALIVE_ACK is lost */
case GPRS_NS2_ST_ALIVE:
case GPRS_NS2_ST_UNBLOCKED:
- gprs_ns2_recv_unitdata(fi, data);
- break;
+ gprs_ns2_recv_unitdata(fi, msg);
+ return;
}
+
+ msgb_free(msg);
+ break;
+ case GPRS_NS2_EV_FORCE_UNCONFIGURED:
+ /* Force the NSVC back to its initial state */
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_UNCONFIGURED, 0, 0);
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_START, NULL);
+ return;
break;
}
}
+static void gprs_ns2_vc_fsm_clean(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ osmo_timer_del(&priv->alive.timer);
+}
+
static struct osmo_fsm gprs_ns2_vc_fsm = {
.name = "GPRS-NS2-VC",
.states = gprs_ns2_vc_states,
.num_states = ARRAY_SIZE(gprs_ns2_vc_states),
.allstate_event_mask = S(GPRS_NS2_EV_UNITDATA) |
- S(GPRS_NS2_EV_RESET) |
+ S(GPRS_NS2_EV_RESET) |
S(GPRS_NS2_EV_ALIVE) |
- S(GPRS_NS2_EV_ALIVE_ACK),
+ S(GPRS_NS2_EV_ALIVE_ACK) |
+ S(GPRS_NS2_EV_FORCE_UNCONFIGURED),
.allstate_action = gprs_ns2_vc_fsm_allstate_action,
- .cleanup = NULL,
+ .cleanup = gprs_ns2_vc_fsm_clean,
.timer_cb = gprs_ns2_vc_fsm_timer_cb,
/* .log_subsys = DNS, "is not constant" */
.event_names = gprs_ns2_vc_event_names,
@@ -562,11 +605,11 @@ static struct osmo_fsm gprs_ns2_vc_fsm = {
* \param ctx
* \param vc
* \param id a char representation of the virtual curcuit
- * \param initiater initiater is the site which starts the connection. Usually the BSS.
+ * \param initiator initiator is the site which starts the connection. Usually the BSS.
* \return NULL on error, otherwise the fsm
*/
struct osmo_fsm_inst *gprs_ns2_vc_fsm_alloc(struct gprs_ns2_vc *nsvc,
- const char *id, bool initiater)
+ const char *id, bool initiator)
{
struct osmo_fsm_inst *fi;
struct gprs_ns2_vc_priv *priv;
@@ -578,7 +621,8 @@ struct osmo_fsm_inst *gprs_ns2_vc_fsm_alloc(struct gprs_ns2_vc *nsvc,
nsvc->fi = fi;
priv = fi->priv = talloc_zero(fi, struct gprs_ns2_vc_priv);
priv->nsvc = nsvc;
- priv->initiater = initiater;
+ priv->initiate_reset = initiator;
+ priv->initiate_block = initiator;
osmo_timer_setup(&priv->alive.timer, alive_timeout_handler, fi);
@@ -596,6 +640,14 @@ int gprs_ns2_vc_fsm_start(struct gprs_ns2_vc *nsvc)
return 0;
}
+/*! Reset a NS-VC FSM.
+ * \param nsvc the virtual circuit
+ * \return 0 on success; negative on error */
+int gprs_ns2_vc_force_unconfigured(struct gprs_ns2_vc *nsvc)
+{
+ return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_FORCE_UNCONFIGURED, NULL);
+}
+
/*! entry point for messages from the driver/VL
* \param nsvc virtual circuit on which the message was received
* \param msg message that was received
@@ -605,6 +657,7 @@ int gprs_ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed
{
struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
struct osmo_fsm_inst *fi = nsvc->fi;
+ int rc = 0;
uint8_t cause;
/* TODO: 7.2: on UNBLOCK/BLOCK: check if NS-VCI is correct,
@@ -614,7 +667,8 @@ int gprs_ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed
if (gprs_ns2_validate(nsvc, nsh->pdu_type, msg, tp, &cause)) {
if (nsh->pdu_type != NS_PDUT_STATUS) {
- return ns2_tx_status(nsvc, cause, 0, msg);
+ rc = ns2_tx_status(nsvc, cause, 0, msg);
+ goto out;
}
}
@@ -644,15 +698,19 @@ int gprs_ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed
osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_ALIVE_ACK, tp);
break;
case NS_PDUT_UNITDATA:
+ /* UNITDATA have to free msg because it might send the msg layer upwards */
osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_UNITDATA, msg);
- break;
+ return 0;
default:
LOGP(DLNS, LOGL_ERROR, "NSEI=%u Rx unknown NS PDU type %s\n", nsvc->nse->nsei,
get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
return -EINVAL;
}
- return 0;
+out:
+ msgb_free(msg);
+
+ return rc;
}
/*! is the given NS-VC unblocked? */
diff --git a/src/gb/gprs_ns2_vty.c b/src/gb/gprs_ns2_vty.c
index a457361a..43e9c2c3 100644
--- a/src/gb/gprs_ns2_vty.c
+++ b/src/gb/gprs_ns2_vty.c
@@ -31,9 +31,11 @@
#include <stdint.h>
#include <arpa/inet.h>
+#include <net/if.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/byteswap.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
@@ -41,6 +43,7 @@
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/socket.h>
+#include <osmocom/gprs/frame_relay.h>
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/vty/vty.h>
@@ -51,6 +54,8 @@
#include "gprs_ns2_internal.h"
+#define SHOW_NS_STR "Display information about the NS protocol\n"
+
struct ns2_vty_priv {
/* global listen */
struct osmo_sockaddr_str udp;
@@ -70,19 +75,26 @@ struct ns2_vty_vc {
struct llist_head list;
struct osmo_sockaddr_str remote;
- enum gprs_ns_ll ll;
+ enum gprs_ns2_ll ll;
/* old vty code doesnt support multiple NSVCI per NSEI */
uint16_t nsei;
uint16_t nsvci;
uint16_t frdlci;
+ struct {
+ enum osmo_fr_role role;
+ } fr;
+
+ char netif[IF_NAMESIZE];
+
bool remote_end_is_sgsn;
bool configured;
};
static struct gprs_ns2_inst *vty_nsi = NULL;
static struct ns2_vty_priv priv;
+static struct osmo_fr_network *vty_fr_network = NULL;
/* FIXME: this should go to some common file as it is copied
* in vty_interface.c of the BSC */
@@ -98,6 +110,18 @@ static const struct value_string gprs_ns_timer_strs[] = {
{ 0, NULL }
};
+static void log_set_nse_filter(struct log_target *target,
+ struct gprs_ns2_nse *nse)
+{
+ if (nse) {
+ target->filter_map |= (1 << LOG_FLT_GB_NSE);
+ target->filter_data[LOG_FLT_GB_NSE] = nse;
+ } else if (target->filter_data[LOG_FLT_GB_NSE]) {
+ target->filter_map = ~(1 << LOG_FLT_GB_NSE);
+ target->filter_data[LOG_FLT_GB_NSE] = NULL;
+ }
+}
+
static void log_set_nsvc_filter(struct log_target *target,
struct gprs_ns2_vc *nsvc)
{
@@ -201,7 +225,7 @@ static int config_write_ns(struct vty *vty)
VTY_NEWLINE);
switch (vtyvc->ll) {
- case GPRS_NS_LL_UDP:
+ case GPRS_NS2_LL_UDP:
vty_out(vty, " nse %u encapsulation udp%s", vtyvc->nsei, VTY_NEWLINE);
vty_out(vty, " nse %u remote-ip %s%s",
vtyvc->nsei,
@@ -211,7 +235,7 @@ static int config_write_ns(struct vty *vty)
vtyvc->nsei, vtyvc->remote.port,
VTY_NEWLINE);
break;
- case GPRS_NS_LL_FR_GRE:
+ case GPRS_NS2_LL_FR_GRE:
vty_out(vty, " nse %u encapsulation framerelay-gre%s",
vtyvc->nsei, VTY_NEWLINE);
vty_out(vty, " nse %u remote-ip %s%s",
@@ -222,6 +246,11 @@ static int config_write_ns(struct vty *vty)
vtyvc->nsei, vtyvc->frdlci,
VTY_NEWLINE);
break;
+ case GPRS_NS2_LL_FR:
+ vty_out(vty, " nse %u fr %s dlci %u%s",
+ vtyvc->nsei, vtyvc->netif, vtyvc->frdlci,
+ VTY_NEWLINE);
+ break;
default:
break;
}
@@ -245,43 +274,22 @@ DEFUN(cfg_ns, cfg_ns_cmd,
static void dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
{
- struct osmo_sockaddr_str remote;
- struct osmo_sockaddr_str local;
- const struct osmo_sockaddr *sockaddr;
-
- switch (nsvc->ll) {
- case GPRS_NS_LL_UDP: {
- sockaddr = gprs_ns2_ip_vc_remote(nsvc);
- if (!sockaddr) {
- vty_out(vty, "unknown");
- break;
- }
-
- if (osmo_sockaddr_str_from_sockaddr(
- &remote,
- &sockaddr->u.sas)) {
- vty_out(vty, "unknown");
- break;
- }
-
- vty_out(vty, "%s:%u <> %s:%u", local.ip, local.port, remote.ip, remote.port);
- break;
- }
- case GPRS_NS_LL_FR_GRE:
- /* TODO: implement dump_nse for FR GRE */
- case GPRS_NS_LL_E1:
- /* TODO: implement dump_nse for E1 */
- break;
- }
+ char nsvci_str[32];
- vty_out(vty, "Remote: %s ",
- gprs_ns2_ll_str(nsvc));
+ if (nsvc->nsvci_is_valid)
+ snprintf(nsvci_str, sizeof(nsvci_str), "%05u", nsvc->nsvci);
+ else
+ snprintf(nsvci_str, sizeof(nsvci_str), "none");
- vty_out(vty, "%s%s", nsvc->ll == GPRS_NS_LL_UDP ? "UDP" : "FR-GRE", VTY_NEWLINE);
+ vty_out(vty, " NSVCI %s: %s %s data_weight=%u sig_weight=%u %s%s", nsvci_str,
+ osmo_fsm_inst_state_name(nsvc->fi),
+ nsvc->persistent ? "PERSIST" : "DYNAMIC",
+ nsvc->data_weight, nsvc->sig_weight,
+ gprs_ns2_ll_str(nsvc), VTY_NEWLINE);
if (stats) {
- vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
- vty_out_stat_item_group(vty, " ", nsvc->statg);
+ vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
+ vty_out_stat_item_group(vty, " ", nsvc->statg);
}
}
@@ -289,8 +297,8 @@ static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats
{
struct gprs_ns2_vc *nsvc;
- vty_out(vty, "NSEI %5u%s",
- nse->nsei, VTY_NEWLINE);
+ vty_out(vty, "NSEI %05u: %s, %s%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
+ nse->alive ? "ALIVE" : "DEAD", VTY_NEWLINE);
gprs_ns2_sns_dump_vty(vty, nse, stats);
llist_for_each_entry(nsvc, &nse->nsvc, list) {
@@ -309,48 +317,72 @@ static void dump_bind(struct vty *vty, const struct gprs_ns2_vc_bind *bind, bool
bind->dump_vty(bind, vty, stats);
}
-static void dump_ns(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
+static void dump_ns_bind(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats)
{
struct gprs_ns2_vc_bind *bind;
- struct gprs_ns2_nse *nse;
llist_for_each_entry(bind, &nsi->binding, list) {
dump_bind(vty, bind, stats);
}
+}
+
+
+static void dump_ns_entities(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
+{
+ struct gprs_ns2_nse *nse;
llist_for_each_entry(nse, &nsi->nse, list) {
dump_nse(vty, nse, stats, persistent_only);
}
+}
+/* Backwards compatibility, among other things for the TestVTYGbproxy which expects
+ * 'show ns' to output something about binds */
+DEFUN_HIDDEN(show_ns, show_ns_cmd, "show ns",
+ SHOW_STR SHOW_NS_STR)
+{
+ dump_ns_entities(vty, vty_nsi, false, false);
+ dump_ns_bind(vty, vty_nsi, false);
+ return CMD_SUCCESS;
}
-DEFUN(show_ns, show_ns_cmd, "show ns",
- SHOW_STR "Display information about the NS protocol")
+
+DEFUN(show_ns_binds, show_ns_binds_cmd, "show ns binds [stats]",
+ SHOW_STR SHOW_NS_STR
+ "Display information about the NS protocol binds\n"
+ "Include statistic\n")
{
- dump_ns(vty, vty_nsi, false, false);
+ bool stats = false;
+ if (argc > 0)
+ stats = true;
+
+ dump_ns_bind(vty, vty_nsi, stats);
return CMD_SUCCESS;
}
-DEFUN(show_ns_stats, show_ns_stats_cmd, "show ns stats",
- SHOW_STR
- "Display information about the NS protocol\n"
+DEFUN(show_ns_entities, show_ns_entities_cmd, "show ns entities [stats]",
+ SHOW_STR SHOW_NS_STR
+ "Display information about the NS protocol entities (NSEs)\n"
"Include statistics\n")
{
- dump_ns(vty, vty_nsi, true, false);
+ bool stats = false;
+ if (argc > 0)
+ stats = true;
+
+ dump_ns_entities(vty, vty_nsi, stats, false);
return CMD_SUCCESS;
}
DEFUN(show_ns_pers, show_ns_pers_cmd, "show ns persistent",
- SHOW_STR
- "Display information about the NS protocol\n"
+ SHOW_STR SHOW_NS_STR
"Show only persistent NS\n")
{
- dump_ns(vty, vty_nsi, true, true);
+ dump_ns_entities(vty, vty_nsi, true, true);
return CMD_SUCCESS;
}
DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
- SHOW_STR "Display information about the NS protocol\n"
+ SHOW_STR SHOW_NS_STR
"Select one NSE by its NSE Identifier\n"
"Select one NSE by its NS-VC Identifier\n"
"The Identifier of selected type\n"
@@ -386,8 +418,77 @@ DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
return CMD_SUCCESS;
}
+static int nsvc_force_unconf_cb(struct gprs_ns2_vc *nsvc, void *ctx)
+{
+ gprs_ns2_vc_force_unconfigured(nsvc);
+ return 0;
+}
+
+DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
+ "nsvc nsei <0-65535> force-unconfigured",
+ "NS Virtual Connection\n"
+ "The NSEI\n"
+ "Reset the NSVCs back to initial state\n"
+ )
+{
+ struct gprs_ns2_inst *nsi = vty_nsi;
+ struct gprs_ns2_nse *nse;
+
+ uint16_t id = atoi(argv[0]);
+
+ nse = gprs_ns2_nse_by_nsei(nsi, id);
+ if (!nse) {
+ vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Perform the operation for all nsvc */
+ gprs_ns2_nse_foreach_nsvc(nse, nsvc_force_unconf_cb, NULL);
+
+ return CMD_SUCCESS;
+}
+
#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n"
+DEFUN(cfg_nse_fr, cfg_nse_fr_cmd,
+ "nse <0-65535> nsvci <0-65535> (fr|frnet) NETIF dlci <0-1023>",
+ NSE_CMD_STR
+ "NS Virtual Connection\n"
+ "NS Virtual Connection ID (NSVCI)\n"
+ "Frame Relay User-Side\n"
+ "Frame Relay Network-Side\n"
+ IFNAME_STR
+ "Data Link connection identifier\n"
+ "Data Link connection identifier\n"
+ )
+{
+ struct ns2_vty_vc *vtyvc;
+
+ uint16_t nsei = atoi(argv[0]);
+ uint16_t nsvci = atoi(argv[1]);
+ const char *role = argv[2];
+ const char *name = argv[3];
+ uint16_t dlci = atoi(argv[4]);
+
+ vtyvc = vtyvc_by_nsei(nsei, true);
+ if (!vtyvc) {
+ vty_out(vty, "Can not allocate space %s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(role, "fr"))
+ vtyvc->fr.role = FR_ROLE_USER_EQUIPMENT;
+ else if (!strcmp(role, "frnet"))
+ vtyvc->fr.role = FR_ROLE_NETWORK_EQUIPMENT;
+
+ osmo_strlcpy(vtyvc->netif, name, sizeof(vtyvc->netif));
+ vtyvc->frdlci = dlci;
+ vtyvc->nsvci = nsvci;
+ vtyvc->ll = GPRS_NS2_LL_FR;
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
"nse <0-65535> nsvci <0-65535>",
NSE_CMD_STR
@@ -454,13 +555,16 @@ DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
}
DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
- "nse <0-65535> fr-dlci <16-1007>",
+ "nse <0-65535> nsvci <0-65535> fr-dlci <16-1007>",
NSE_CMD_STR
+ "NS Virtual Connection\n"
+ "NS Virtual Connection ID (NSVCI)\n"
"Frame Relay DLCI\n"
"Frame Relay DLCI Number\n")
{
uint16_t nsei = atoi(argv[0]);
- uint16_t dlci = atoi(argv[1]);
+ uint16_t nsvci = atoi(argv[1]);
+ uint16_t dlci = atoi(argv[2]);
struct ns2_vty_vc *vtyvc;
vtyvc = vtyvc_by_nsei(nsei, true);
@@ -469,12 +573,8 @@ DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd,
return CMD_WARNING;
}
- if (vtyvc->ll != GPRS_NS_LL_FR_GRE) {
- vty_out(vty, "Warning: seting FR DLCI on non-FR NSE%s",
- VTY_NEWLINE);
- }
-
vtyvc->frdlci = dlci;
+ vtyvc->nsvci = nsvci;
return CMD_SUCCESS;
}
@@ -495,9 +595,9 @@ DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd,
}
if (!strcmp(argv[1], "udp"))
- vtyvc->ll = GPRS_NS_LL_UDP;
+ vtyvc->ll = GPRS_NS2_LL_UDP;
else
- vtyvc->ll = GPRS_NS_LL_FR_GRE;
+ vtyvc->ll = GPRS_NS2_LL_FR_GRE;
return CMD_SUCCESS;
}
@@ -668,7 +768,37 @@ DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd,
/* TODO: allow vty to reset/block/unblock nsvc/nsei */
-/* TODO: add filter for NSEI as ns1 code does */
+DEFUN(logging_fltr_nse,
+ logging_fltr_nse_cmd,
+ "logging filter nse nsei <0-65535>",
+ LOGGING_STR FILTER_STR
+ "Filter based on NS Entity\n"
+ "Identify NSE by NSEI\n"
+ "Numeric identifier\n")
+{
+ struct log_target *tgt;
+ struct gprs_ns2_nse *nse;
+ uint16_t id = atoi(argv[0]);
+
+ log_tgt_mutex_lock();
+ tgt = osmo_log_vty2tgt(vty);
+ if (!tgt) {
+ log_tgt_mutex_unlock();
+ return CMD_WARNING;
+ }
+
+ nse = gprs_ns2_nse_by_nsei(vty_nsi, id);
+ if (!nse) {
+ vty_out(vty, "No NSE by that identifier%s", VTY_NEWLINE);
+ log_tgt_mutex_unlock();
+ return CMD_WARNING;
+ }
+
+ log_set_nse_filter(tgt, nse);
+ log_tgt_mutex_unlock();
+ return CMD_SUCCESS;
+}
+
/* TODO: add filter for single connection by description */
DEFUN(logging_fltr_nsvc,
logging_fltr_nsvc_cmd,
@@ -680,7 +810,7 @@ DEFUN(logging_fltr_nsvc,
{
struct log_target *tgt;
struct gprs_ns2_vc *nsvc;
- uint16_t id = atoi(argv[1]);
+ uint16_t id = atoi(argv[0]);
log_tgt_mutex_lock();
tgt = osmo_log_vty2tgt(vty);
@@ -726,15 +856,21 @@ int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi,
vty_elements_installed = true;
install_lib_element_ve(&show_ns_cmd);
- install_lib_element_ve(&show_ns_stats_cmd);
+ install_lib_element_ve(&show_ns_binds_cmd);
+ install_lib_element_ve(&show_ns_entities_cmd);
install_lib_element_ve(&show_ns_pers_cmd);
install_lib_element_ve(&show_nse_cmd);
+ install_lib_element_ve(&logging_fltr_nse_cmd);
install_lib_element_ve(&logging_fltr_nsvc_cmd);
+ install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
+
+ install_lib_element(CFG_LOG_NODE, &logging_fltr_nse_cmd);
install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
install_node(&ns_node, config_write_ns);
+ install_lib_element(L_NS_NODE, &cfg_nse_fr_cmd);
install_lib_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
install_lib_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
install_lib_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
@@ -762,10 +898,11 @@ int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi,
*/
int gprs_ns2_vty_create() {
struct ns2_vty_vc *vtyvc;
- struct gprs_ns2_vc_bind *bind;
+ struct gprs_ns2_vc_bind *bind, *fr;
struct gprs_ns2_nse *nse;
struct gprs_ns2_vc *nsvc;
struct osmo_sockaddr sockaddr;
+ int rc = 0;
if (!vty_nsi)
return -1;
@@ -786,24 +923,33 @@ int gprs_ns2_vty_create() {
/* create vcs */
llist_for_each_entry(vtyvc, &priv.vtyvc, list) {
- if (strlen(vtyvc->remote.ip) == 0) {
- /* Invalid IP for VC */
- continue;
- }
+ /* validate settings */
+ switch (vtyvc->ll) {
+ case GPRS_NS2_LL_UDP:
+ if (strlen(vtyvc->remote.ip) == 0) {
+ /* Invalid IP for VC */
+ continue;
+ }
- if (!vtyvc->remote.port) {
- /* Invalid port for VC */
- continue;
- }
+ if (!vtyvc->remote.port) {
+ /* Invalid port for VC */
+ continue;
+ }
- if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
- /* Invalid sockaddr for VC */
+ if (osmo_sockaddr_str_to_sockaddr(&vtyvc->remote, &sockaddr.u.sas)) {
+ /* Invalid sockaddr for VC */
+ continue;
+ }
+ break;
+ case GPRS_NS2_LL_FR:
+ break;
+ case GPRS_NS2_LL_FR_GRE:
continue;
}
nse = gprs_ns2_nse_by_nsei(vty_nsi, vtyvc->nsei);
if (!nse) {
- nse = gprs_ns2_create_nse(vty_nsi, vtyvc->nsei);
+ nse = gprs_ns2_create_nse(vty_nsi, vtyvc->nsei, vtyvc->ll);
if (!nse) {
/* Could not create NSE for VTY */
continue;
@@ -811,15 +957,45 @@ int gprs_ns2_vty_create() {
}
nse->persistent = true;
- nsvc = gprs_ns2_ip_connect(bind,
- &sockaddr,
- nse,
- vtyvc->nsvci);
- if (!nsvc) {
- /* Could not create NSVC, connect failed */
+ switch (vtyvc->ll) {
+ case GPRS_NS2_LL_UDP:
+ nsvc = gprs_ns2_ip_connect(bind,
+ &sockaddr,
+ nse,
+ vtyvc->nsvci);
+ if (!nsvc) {
+ /* Could not create NSVC, connect failed */
+ continue;
+ }
+ nsvc->persistent = true;
+ break;
+ case GPRS_NS2_LL_FR: {
+ if (vty_fr_network == NULL) {
+ /* TODO: add a switch for BSS/SGSN/gbproxy */
+ vty_fr_network = osmo_fr_network_alloc(vty_nsi);
+ }
+ fr = gprs_ns2_fr_bind_by_netif(
+ vty_nsi,
+ vtyvc->netif);
+ if (!fr) {
+ rc = gprs_ns2_fr_bind(vty_nsi, vtyvc->netif, vty_fr_network, vtyvc->fr.role, &fr);
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_ERROR, "Can not create fr bind on device %s err: %d\n", vtyvc->netif, rc);
+ return rc;
+ }
+ }
+
+ nsvc = gprs_ns2_fr_connect(fr, vtyvc->nsei, vtyvc->nsvci, vtyvc->frdlci);
+ if (!nsvc) {
+ /* Could not create NSVC, connect failed */
+ continue;
+ }
+ nsvc->persistent = true;
+ break;
+ }
+ case GPRS_NS2_LL_FR_GRE:
continue;
}
- nsvc->persistent = true;
}
diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map
index c9ba012c..2c4e897e 100644
--- a/src/gb/libosmogb.map
+++ b/src/gb/libosmogb.map
@@ -17,6 +17,7 @@ bssgp_set_bssgp_callback;
bssgp_tx_bvc_block;
bssgp_tx_bvc_reset;
bssgp_tx_bvc_reset2;
+bssgp_tx_bvc_reset_nsei_bvci;
bssgp_tx_bvc_unblock;
bssgp_tx_fc_bvc;
bssgp_tx_fc_ms;
@@ -44,6 +45,14 @@ bssgp_tx_paging;
bssgp_vty_init;
bssgp_nsi;
+osmo_fr_network_alloc;
+osmo_fr_link_alloc;
+osmo_fr_link_free;
+osmo_fr_dlc_alloc;
+osmo_fr_rx;
+osmo_fr_tx_dlc;
+osmo_fr_role_names;
+
gprs_ns_signal_ns_names;
gprs_ns_pdu_strings;
gprs_ns_cause_str;
@@ -72,8 +81,9 @@ gprs_ns_ll_copy;
gprs_ns_ll_clear;
gprs_ns_msgb_alloc;
+gprs_ns2_aff_cause_prim_strs;
gprs_ns2_bind_set_mode;
-gprs_ns2_cause_str;
+gprs_ns2_cause_strs;
gprs_ns2_create_nse;
gprs_ns2_dynamic_create_nse;
gprs_ns2_find_vc_by_sockaddr;
@@ -84,6 +94,14 @@ gprs_ns2_free_nse;
gprs_ns2_free_nses;
gprs_ns2_free_nsvc;
gprs_ns2_frgre_bind;
+gprs_ns2_fr_bind;
+gprs_ns2_fr_bind_netif;
+gprs_ns2_fr_bind_by_netif;
+gprs_ns2_fr_connect;
+gprs_ns2_fr_nsvc_by_dlci;
+gprs_ns2_fr_nsvc_dlci;
+gprs_ns2_is_fr_bind;
+gprs_ns2_find_vc_by_dlci;
gprs_ns2_instantiate;
gprs_ns2_ip_bind;
gprs_ns2_ip_bind_by_sockaddr;
@@ -101,12 +119,15 @@ gprs_ns2_is_ip_bind;
gprs_ns2_ll_str;
gprs_ns2_ll_str_buf;
gprs_ns2_ll_str_c;
+gprs_ns2_lltype_strs;
gprs_ns2_nse_by_nsei;
gprs_ns2_nse_foreach_nsvc;
gprs_ns2_nse_nsei;
gprs_ns2_nse_sns_remote;
gprs_ns2_nsvc_by_nsvci;
gprs_ns2_nsvc_by_sockaddr;
+gprs_ns2_nsvc_state_name;
+gprs_ns2_prim_strs;
gprs_ns2_recv_prim;
gprs_ns2_reset_persistent_nsvcs;
gprs_ns2_start_alive_all_nsvcs;
diff --git a/src/gsm/bts_features.c b/src/gsm/bts_features.c
index 9a5188b2..93703aaa 100644
--- a/src/gsm/bts_features.c
+++ b/src/gsm/bts_features.c
@@ -41,6 +41,7 @@ const struct value_string osmo_bts_features_descs[] = {
{ BTS_FEAT_ETWS_PN, "ETWS Primary Notification via PCH" },
{ BTS_FEAT_PAGING_COORDINATION, "BSS Paging Coordination" },
{ BTS_FEAT_IPV6_NSVC, "NSVC IPv6" },
+ { BTS_FEAT_ACCH_REP, "FACCH/SACCH Repeation" },
{ 0, NULL }
};
diff --git a/src/gsm/gsm0411_smr.c b/src/gsm/gsm0411_smr.c
index 02b41995..03cf0051 100644
--- a/src/gsm/gsm0411_smr.c
+++ b/src/gsm/gsm0411_smr.c
@@ -131,7 +131,7 @@ const struct value_string gsm411_rp_cause_strs[] = {
{ GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" },
{ GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" },
{ GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" },
- { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" },
+ { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existent" },
{ GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" },
{ GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" },
{ GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" },
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c
index eb1d055b..c497c745 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -164,13 +164,16 @@ static const struct value_string rr_cause_names[] = {
{ GSM48_RR_CAUSE_ABNORMAL_TIMER, "Abnormal release, timer expired" },
{ GSM48_RR_CAUSE_ABNORMAL_NOACT, "Abnormal release, no activity on radio path" },
{ GSM48_RR_CAUSE_PREMPTIVE_REL, "Preemptive release" },
+ { GSM48_RR_CAUSE_UTRAN_CFG_UNK, "UTRAN configuration unknown" },
{ GSM48_RR_CAUSE_HNDOVER_IMP, "Handover impossible, timing advance out of range" },
{ GSM48_RR_CAUSE_CHAN_MODE_UNACCT, "Channel mode unacceptable" },
{ GSM48_RR_CAUSE_FREQ_NOT_IMPL, "Frequency not implemented" },
+ { GSM48_RR_CAUSE_LEAVE_GROUP_CA, "Originator or talker leaving group call area" },
+ { GSM48_RR_CAUSE_LOW_LEVEL_FAIL, "Lower layer failure" },
{ GSM48_RR_CAUSE_CALL_CLEARED, "Call already cleared" },
{ GSM48_RR_CAUSE_SEMANT_INCORR, "Semantically incorrect message" },
{ GSM48_RR_CAUSE_INVALID_MAND_INF, "Invalid mandatory information" },
- { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existant or not implemented" },
+ { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existent or not implemented" },
{ GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT, "Message type not compatible with protocol state" },
{ GSM48_RR_CAUSE_COND_IE_ERROR, "Conditional IE error" },
{ GSM48_RR_CAUSE_NO_CELL_ALLOC_A, "No cell allocation available" },
diff --git a/src/gsm/gsm48_ie.c b/src/gsm/gsm48_ie.c
index 31028ba4..6070e5cc 100644
--- a/src/gsm/gsm48_ie.c
+++ b/src/gsm/gsm48_ie.c
@@ -34,6 +34,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/mncc.h>
+#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48_ie.h>
@@ -1299,4 +1300,249 @@ int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
return 0;
}
+
+/*! Decode 3GPP TS 24.008 Mobile Station Classmark 3 (10.5.1.7).
+ * \param[out] classmark3_out user provided memory to store decoded classmark3.
+ * \param[in] classmark3 pointer to memory that contains the raw classmark bits.
+ * \param[in] classmark3_len length in bytes of the memory where classmark3 points to.
+ * \returns 0 on success; negative on error. */
+int gsm48_decode_classmark3(struct gsm48_classmark3 *classmark3_out,
+ const uint8_t *classmark3, size_t classmark3_len)
+{
+ struct bitvec bv;
+ uint8_t data[255];
+ struct gsm48_classmark3 *cm3 = classmark3_out;
+
+ /* if cm3 gets extended by spec, it will be truncated, but 255 bytes
+ * should be more than enough. */
+ if (classmark3_len > sizeof(data))
+ classmark3_len = sizeof(data);
+
+ memset(&bv, 0, sizeof(bv));
+ memset(data, 0, sizeof(data));
+ memset(classmark3_out, 0, sizeof(*classmark3_out));
+
+ memcpy(data, classmark3, classmark3_len);
+ bv.data = (uint8_t*) data;
+ bv.data_len = sizeof(data);
+
+ /* Parse bit vector, see also: 3GPP TS 24.008, section 10.5.1.7 */
+ bitvec_get_uint(&bv, 1);
+ cm3->mult_band_supp = bitvec_get_uint(&bv, 3);
+ switch (cm3->mult_band_supp) {
+ case 0x00:
+ cm3->a5_bits = bitvec_get_uint(&bv, 4);
+ break;
+ case 0x05:
+ case 0x06:
+ cm3->a5_bits = bitvec_get_uint(&bv, 4);
+ cm3->assoc_radio_cap_2 = bitvec_get_uint(&bv, 4);
+ cm3->assoc_radio_cap_1 = bitvec_get_uint(&bv, 4);
+ break;
+ case 0x01:
+ case 0x02:
+ case 0x04:
+ cm3->a5_bits = bitvec_get_uint(&bv, 4);
+ bitvec_get_uint(&bv, 4);
+ cm3->assoc_radio_cap_1 = bitvec_get_uint(&bv, 4);
+ break;
+ default:
+ return -1;
+ }
+
+ cm3->r_support.present = bitvec_get_uint(&bv, 1);
+ if (cm3->r_support.present)
+ cm3->r_support.r_gsm_assoc_radio_cap = bitvec_get_uint(&bv, 3);
+
+ cm3->hscsd_mult_slot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->hscsd_mult_slot_cap.present)
+ cm3->hscsd_mult_slot_cap.mslot_class = bitvec_get_uint(&bv, 5);
+
+ cm3->ucs2_treatment = bitvec_get_uint(&bv, 1);
+ cm3->extended_meas_cap = bitvec_get_uint(&bv, 1);
+
+ cm3->ms_meas_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->ms_meas_cap.present) {
+ cm3->ms_meas_cap.sms_value = bitvec_get_uint(&bv, 4);
+ cm3->ms_meas_cap.sm_value = bitvec_get_uint(&bv, 4);
+ }
+
+ cm3->ms_pos_method_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->ms_pos_method_cap.present)
+ cm3->ms_pos_method_cap.method = bitvec_get_uint(&bv, 5);
+
+ cm3->ecsd_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->ecsd_multislot_cap.present)
+ cm3->ecsd_multislot_cap.mslot_class = bitvec_get_uint(&bv, 5);
+
+ cm3->psk8_struct.present = bitvec_get_uint(&bv, 1);
+ if (cm3->psk8_struct.present) {
+ cm3->psk8_struct.mod_cap = bitvec_get_uint(&bv, 1);
+
+ cm3->psk8_struct.rf_pwr_cap_1.present = bitvec_get_uint(&bv, 1);
+ if (cm3->psk8_struct.rf_pwr_cap_1.present) {
+ cm3->psk8_struct.rf_pwr_cap_1.value =
+ bitvec_get_uint(&bv, 2);
+ }
+
+ cm3->psk8_struct.rf_pwr_cap_2.present = bitvec_get_uint(&bv, 1);
+ if (cm3->psk8_struct.rf_pwr_cap_2.present) {
+ cm3->psk8_struct.rf_pwr_cap_2.value =
+ bitvec_get_uint(&bv, 2);
+ }
+ }
+
+ cm3->gsm_400_bands_supp.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_400_bands_supp.present) {
+ cm3->gsm_400_bands_supp.value = bitvec_get_uint(&bv, 2);
+ if (cm3->gsm_400_bands_supp.value == 0x00)
+ return -1;
+ cm3->gsm_400_bands_supp.assoc_radio_cap =
+ bitvec_get_uint(&bv, 4);
+ }
+
+ cm3->gsm_850_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_850_assoc_radio_cap.present)
+ cm3->gsm_850_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->gsm_1900_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_1900_assoc_radio_cap.present)
+ cm3->gsm_1900_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->umts_fdd_rat_cap = bitvec_get_uint(&bv, 1);
+ cm3->umts_tdd_rat_cap = bitvec_get_uint(&bv, 1);
+ cm3->cdma200_rat_cap = bitvec_get_uint(&bv, 1);
+
+ cm3->dtm_gprs_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->dtm_gprs_multislot_cap.present) {
+ cm3->dtm_gprs_multislot_cap.mslot_class = bitvec_get_uint(&bv, 2);
+ cm3->dtm_gprs_multislot_cap.single_slot_dtm =
+ bitvec_get_uint(&bv, 1);
+ cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present =
+ bitvec_get_uint(&bv, 1);
+ if (cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present)
+ cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.
+ mslot_class = bitvec_get_uint(&bv, 2);
+ }
+
+ /* Release 4 starts here. */
+ cm3->single_band_supp.present = bitvec_get_uint(&bv, 1);
+ if (cm3->single_band_supp.present)
+ cm3->single_band_supp.value = bitvec_get_uint(&bv, 4);
+
+ cm3->gsm_750_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_750_assoc_radio_cap.present)
+ cm3->gsm_750_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->umts_1_28_mcps_tdd_rat_cap = bitvec_get_uint(&bv, 1);
+ cm3->geran_feature_package = bitvec_get_uint(&bv, 1);
+
+ cm3->extended_dtm_gprs_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->extended_dtm_gprs_multislot_cap.present) {
+ cm3->extended_dtm_gprs_multislot_cap.mslot_class =
+ bitvec_get_uint(&bv, 2);
+ cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.present =
+ bitvec_get_uint(&bv, 1);
+ if (cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.present)
+ cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.mslot_class =
+ bitvec_get_uint(&bv, 2);
+ }
+
+ /* Release 5 starts here */
+ cm3->high_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->high_multislot_cap.present)
+ cm3->high_multislot_cap.value = bitvec_get_uint(&bv, 2);
+
+ /* This used to be the GERAN Iu mode support bit, but the newer spec
+ * releases say that it should not be used (always zero), however
+ * we will just ignore tha state of this bit. */
+ bitvec_get_uint(&bv, 1);
+
+ cm3->geran_feature_package_2 = bitvec_get_uint(&bv, 1);
+ cm3->gmsk_multislot_power_prof = bitvec_get_uint(&bv, 2);
+ cm3->psk8_multislot_power_prof = bitvec_get_uint(&bv, 2);
+
+ /* Release 6 starts here */
+ cm3->t_gsm_400_bands_supp.present = bitvec_get_uint(&bv, 1);
+ if (cm3->t_gsm_400_bands_supp.present) {
+ cm3->t_gsm_400_bands_supp.value = bitvec_get_uint(&bv, 2);
+ cm3->t_gsm_400_bands_supp.assoc_radio_cap =
+ bitvec_get_uint(&bv, 4);
+ }
+
+ /* This used to be T-GSM 900 associated radio capability, but the
+ * newer spec releases say that this bit should not be used, but if
+ * it is used by some MS anyway we must assume that there is data
+ * we have to override. */
+ if (bitvec_get_uint(&bv, 1))
+ bitvec_get_uint(&bv, 4);
+
+ cm3->dl_advanced_rx_perf = bitvec_get_uint(&bv, 2);
+ cm3->dtm_enhancements_cap = bitvec_get_uint(&bv, 1);
+
+ cm3->dtm_gprs_high_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->dtm_gprs_high_multislot_cap.present) {
+ cm3->dtm_gprs_high_multislot_cap.mslot_class =
+ bitvec_get_uint(&bv, 3);
+ cm3->dtm_gprs_high_multislot_cap.offset_required =
+ bitvec_get_uint(&bv, 1);
+ cm3->dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.
+ present = bitvec_get_uint(&bv, 1);
+ if (cm3->dtm_gprs_high_multislot_cap.
+ dtm_egprs_high_multislot_cap.present)
+ cm3->dtm_gprs_high_multislot_cap.
+ dtm_egprs_high_multislot_cap.mslot_class =
+ bitvec_get_uint(&bv, 3);
+ }
+
+ cm3->repeated_acch_capability = bitvec_get_uint(&bv, 1);
+
+ /* Release 7 starts here */
+ cm3->gsm_710_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_710_assoc_radio_cap.present)
+ cm3->gsm_710_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->t_gsm_810_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->t_gsm_810_assoc_radio_cap.present)
+ cm3->t_gsm_810_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->ciphering_mode_setting_cap = bitvec_get_uint(&bv, 1);
+ cm3->add_pos_cap = bitvec_get_uint(&bv, 1);
+
+ /* Release 8 starts here */
+ cm3->e_utra_fdd_supp = bitvec_get_uint(&bv, 1);
+ cm3->e_utra_tdd_supp = bitvec_get_uint(&bv, 1);
+ cm3->e_utra_meas_rep_supp = bitvec_get_uint(&bv, 1);
+ cm3->prio_resel_supp = bitvec_get_uint(&bv, 1);
+
+ /* Release 9 starts here */
+ cm3->utra_csg_cells_rep = bitvec_get_uint(&bv, 1);
+
+ cm3->vamos_level = bitvec_get_uint(&bv, 2);
+
+ /* Release 10 starts here */
+ cm3->tighter_capability = bitvec_get_uint(&bv, 2);
+ cm3->sel_ciph_dl_sacch = bitvec_get_uint(&bv, 1);
+
+ /* Release 11 starts here */
+ cm3->cs_ps_srvcc_geran_utra = bitvec_get_uint(&bv, 2);
+ cm3->cs_ps_srvcc_geran_eutra = bitvec_get_uint(&bv, 2);
+
+ cm3->geran_net_sharing = bitvec_get_uint(&bv, 1);
+ cm3->e_utra_wb_rsrq_meas_supp = bitvec_get_uint(&bv, 1);
+
+ /* Release 12 starts here */
+ cm3->er_band_support = bitvec_get_uint(&bv, 1);
+ cm3->utra_mult_band_ind_supp = bitvec_get_uint(&bv, 1);
+ cm3->e_utra_mult_band_ind_supp = bitvec_get_uint(&bv, 1);
+ cm3->extended_tsc_set_cap_supp = bitvec_get_uint(&bv, 1);
+
+ /* Late addition of a release 11 feature */
+ cm3->extended_earfcn_val_range = bitvec_get_uint(&bv, 1);
+
+ return 0;
+}
/*! @} */
diff --git a/src/gsm/gsm_04_08_gprs.c b/src/gsm/gsm_04_08_gprs.c
index 608fa8c1..80325939 100644
--- a/src/gsm/gsm_04_08_gprs.c
+++ b/src/gsm/gsm_04_08_gprs.c
@@ -65,7 +65,7 @@ const struct value_string gsm48_gmm_cause_names_[] = {
{ GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" },
{ GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" },
{ GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL,
- "Message type non-existant or not implemented" },
+ "Message type non-existent or not implemented" },
{ GMM_CAUSE_MSGT_INCOMP_P_STATE,
"Message type not compatible with protocol state" },
{ GMM_CAUSE_IE_NOTEXIST_NOTIMPL,
@@ -105,7 +105,7 @@ const struct value_string gsm48_gsm_cause_names_[] = {
{ GSM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" },
{ GSM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" },
{ GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL,
- "Message type non-existant or not implemented" },
+ "Message type non-existent or not implemented" },
{ GSM_CAUSE_MSGT_INCOMP_P_STATE,
"Message type not compatible with protocol state" },
{ GSM_CAUSE_IE_NOTEXIST_NOTIMPL,
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 031cc200..f339120c 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -339,6 +339,7 @@ gsm48_decode_cccap;
gsm48_decode_connected;
gsm48_decode_facility;
gsm48_decode_freq_list;
+gsm48_decode_classmark3;
gsm48_decode_keypad;
gsm48_decode_lai;
gsm48_decode_notify;
diff --git a/src/gsm/rsl.c b/src/gsm/rsl.c
index c3420b62..2ab49c23 100644
--- a/src/gsm/rsl.c
+++ b/src/gsm/rsl.c
@@ -126,6 +126,7 @@ const struct tlv_definition rsl_att_tlvdef = {
[RSL_IE_TFO_STATUS] = { TLV_TYPE_TV },
[RSL_IE_LLP_APDU] = { TLV_TYPE_TLV },
[RSL_IE_SIEMENS_MRPCI] = { TLV_TYPE_TV },
+ [RSL_IE_OSMO_REP_ACCH_CAP] = { TLV_TYPE_TLV },
[RSL_IE_IPAC_PROXY_UDP] = { TLV_TYPE_FIXED, 2 },
[RSL_IE_IPAC_BSCMPL_TOUT] = { TLV_TYPE_TV },
[RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 },
diff --git a/src/mnl.c b/src/mnl.c
new file mode 100644
index 00000000..a7d3511e
--- /dev/null
+++ b/src/mnl.c
@@ -0,0 +1,116 @@
+/*! \file mnl.c
+ *
+ * This code integrates libmnl (minimal netlink library) into the osmocom select
+ * loop abstraction. It allows other osmocom libraries or application code to
+ * create netlink sockets and subscribe to netlink events via libmnl. The completion
+ * handler / callbacks are dispatched via libosmocore select loop handling.
+ */
+
+/*
+ * (C) 2020 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserverd.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/mnl.h>
+
+#include <libmnl/libmnl.h>
+
+#include <errno.h>
+#include <string.h>
+
+/* osmo_fd call-back for when RTNL socket is readable */
+static int osmo_mnl_fd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ uint8_t buf[MNL_SOCKET_BUFFER_SIZE];
+ struct osmo_mnl *omnl = ofd->data;
+ int rc;
+
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ rc = mnl_socket_recvfrom(omnl->mnls, buf, sizeof(buf));
+ if (rc <= 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error in mnl_socket_recvfrom(): %s\n",
+ strerror(errno));
+ return -EIO;
+ }
+
+ return mnl_cb_run(buf, rc, 0, 0, omnl->mnl_cb, omnl);
+}
+
+/*! create an osmocom-wrapped limnl netlink socket.
+ * \parma[in] ctx talloc context from which to allocate
+ * \param[in] bus netlink socket bus ID (see NETLINK_* constants)
+ * \param[in] groups groups of messages to bind/subscribe to
+ * \param[in] mnl_cb callback function called for each incoming message
+ * \param[in] priv opaque private user data
+ * \returns newly-allocated osmo_mnl or NULL in case of error. */
+struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv)
+{
+ struct osmo_mnl *olm = talloc_zero(ctx, struct osmo_mnl);
+
+ if (!olm)
+ return NULL;
+
+ olm->priv = priv;
+ olm->mnl_cb = mnl_cb;
+ olm->mnls = mnl_socket_open(bus);
+ if (!olm->mnls) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error creating netlink socket for bus %d: %s\n",
+ bus, strerror(errno));
+ goto out_free;
+ }
+
+ if (mnl_socket_bind(olm->mnls, groups, MNL_SOCKET_AUTOPID) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error binding netlink socket for bus %d to groups 0x%x: %s\n",
+ bus, groups, strerror(errno));
+ goto out_close;
+ }
+
+ osmo_fd_setup(&olm->ofd, mnl_socket_get_fd(olm->mnls), OSMO_FD_READ, osmo_mnl_fd_cb, olm, 0);
+
+ if (osmo_fd_register(&olm->ofd)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error registering netlinks socket\n");
+ goto out_close;
+ }
+
+ return olm;
+
+out_close:
+ mnl_socket_close(olm->mnls);
+out_free:
+ talloc_free(olm);
+ return NULL;
+}
+
+/*! destroy an existing osmocom-wrapped mnl netlink socket: Unregister + close + free.
+ * \param[in] omnl osmo_mnl socket previously returned by osmo_mnl_init() */
+void osmo_mnl_destroy(struct osmo_mnl *omnl)
+{
+ if (!omnl)
+ return;
+
+ osmo_fd_unregister(&omnl->ofd);
+ mnl_socket_close(omnl->mnls);
+ talloc_free(omnl);
+}
diff --git a/src/serial.c b/src/serial.c
index 31cb81d1..c3bf5e89 100644
--- a/src/serial.c
+++ b/src/serial.c
@@ -67,7 +67,7 @@ osmo_serial_init(const char *dev, speed_t baudrate)
return -errno;
}
- /* now put it into blcoking mode */
+ /* now put it into blocking mode */
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
dbg_perror("fcntl get flags");
@@ -91,8 +91,10 @@ osmo_serial_init(const char *dev, speed_t baudrate)
goto error;
}
- cfsetispeed(&tio, baudrate);
- cfsetospeed(&tio, baudrate);
+ if (cfsetispeed(&tio, baudrate) < 0)
+ dbg_perror("cfsetispeed()");
+ if (cfsetospeed(&tio, baudrate) < 0)
+ dbg_perror("cfsetospeed()");
tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
tio.c_cflag |= (CREAD | CLOCAL | CS8);
@@ -136,12 +138,15 @@ _osmo_serial_set_baudrate(int fd, speed_t baudrate)
dbg_perror("tcgetattr()");
return -errno;
}
- cfsetispeed(&tio, baudrate);
- cfsetospeed(&tio, baudrate);
+
+ if (cfsetispeed(&tio, baudrate) < 0)
+ dbg_perror("cfsetispeed()");
+ if (cfsetospeed(&tio, baudrate) < 0)
+ dbg_perror("cfsetospeed()");
rc = tcsetattr(fd, TCSANOW, &tio);
if (rc < 0) {
- dbg_perror("tcgetattr()");
+ dbg_perror("tcsetattr()");
return -errno;
}
@@ -240,4 +245,39 @@ osmo_serial_clear_custom_baudrate(int fd)
return 0;
}
+/*! Convert unsigned integer value to speed_t
+ * \param[in] baudrate integer value containing the desired standard baudrate
+ * \param[out] speed the standrd baudrate requested in speed_t format
+ * \returns 0 for success or negative errno.
+ */
+int
+osmo_serial_speed_t(unsigned int baudrate, speed_t *speed)
+{
+ switch(baudrate) {
+ case 0: *speed = B0; break;
+ case 50: *speed = B50; break;
+ case 75: *speed = B75; break;
+ case 110: *speed = B110; break;
+ case 134: *speed = B134; break;
+ case 150: *speed = B150; break;
+ case 200: *speed = B200; break;
+ case 300: *speed = B300; break;
+ case 600: *speed = B600; break;
+ case 1200: *speed = B1200; break;
+ case 1800: *speed = B1800; break;
+ case 2400: *speed = B2400; break;
+ case 4800: *speed = B4800; break;
+ case 9600: *speed = B9600; break;
+ case 19200: *speed = B19200; break;
+ case 38400: *speed = B38400; break;
+ case 57600: *speed = B57600; break;
+ case 115200: *speed = B115200; break;
+ case 230400: *speed = B230400; break;
+ default:
+ *speed = B0;
+ return -EINVAL;
+ }
+ return 0;
+}
+
/*! @} */
diff --git a/src/sim/card_fs_usim.c b/src/sim/card_fs_usim.c
index 4c8f79c4..8f880e75 100644
--- a/src/sim/card_fs_usim.c
+++ b/src/sim/card_fs_usim.c
@@ -348,6 +348,68 @@ static const struct osim_file_desc usim_ef_in_df_hnb[] = {
"Oprator Home NodeB Name"),
};
+/* 31.102 Chapter 4.4.8 */
+static const struct osim_file_desc usim_ef_in_df_prose[] = {
+ EF_LIN_FIX_N(0x4F01, 0x01, "EF.PROSE_MON", F_OPTIONAL, 1, 64,
+ "ProSe Monitoring Parameters"),
+ EF_LIN_FIX_N(0x4F02, 0x02, "EF.PROSE_ANN", F_OPTIONAL, 1, 64,
+ "ProSe Announcing Parameters"),
+ EF_LIN_FIX_N(0x4F03, 0x03, "EF.PROSEFUNC", F_OPTIONAL, 1, 64,
+ "HPLMN ProSe Function"),
+ EF_TRANSP_N(0x4F04, 0x04, "EF.PROSE_RADIO_COM", F_OPTIONAL, 1, 128,
+ "ProSe Direct Communication Radio Parameters"),
+ EF_TRANSP_N(0x4F05, 0x05, "EF.PROSE_RADIO_MON", F_OPTIONAL, 1, 128,
+ "ProSe Direct Discovery Monitoring Radio Parameters"),
+ EF_TRANSP_N(0x4F06, 0x06, "EF.PROSE_RADIO_ANN", F_OPTIONAL, 1, 128,
+ "ProSe Direct Discovery Announcing Radio Parameters"),
+ EF_LIN_FIX_N(0x4F07, 0x07, "EF.PROSE_POLICY", F_OPTIONAL, 1, 64,
+ "ProSe Direct Discovery Announcing Radio Parameters"),
+ EF_LIN_FIX_N(0x4F08, 0x08, "EF.PROSE_PLMN", F_OPTIONAL, 1, 64,
+ "ProSe PLMN Parametes"),
+ EF_TRANSP_N(0x4F09, 0x09, "EF.PROSE_GC", F_OPTIONAL, 8, 64,
+ "ProSe Direct Discovery Announcing Radio Parameters"),
+ EF_TRANSP_N(0x4F10, 0x10, "EF.PST", F_OPTIONAL, 1, 2,
+ "ProSe Service Table"),
+ EF_TRANSP_N(0x4F11, 0x11, "EF.PROSE_UIRC", F_OPTIONAL, 1, 128,
+ "ProSe UsageInformationReportingConfiguration"),
+ EF_LIN_FIX_N(0x4F12, 0x12, "EF.PROSE_GM_DISCOVERY", F_OPTIONAL, 1, 64,
+ "ProSe Group Member Discovery Parameters"),
+ EF_LIN_FIX_N(0x4F13, 0x13, "EF.PROSE_RELAY", F_OPTIONAL, 1, 64,
+ "ProSe Relay Parameters"),
+ EF_TRANSP_N(0x4F14, 0x14, "EF.PROSE_RELAY_DISCOVERY", F_OPTIONAL, 5, 64,
+ "ProSe Relay Discovery Parameters"),
+};
+
+/* 31.102 Chapter 4.4.9 */
+static const struct osim_file_desc usim_ef_in_df_acdc[] = {
+ EF_TRANSP_N(0x4F01, 0x01, "EF.ACDC_LIST", F_OPTIONAL, 1, 128,
+ "ACDC List"),
+};
+
+/* 31.102 Chapter 4.4.11 */
+static const struct osim_file_desc usim_ef_in_df_5gs[] = {
+ EF_TRANSP_N(0x4F01, 0x01, "EF.5GS3GPPLOCI", F_OPTIONAL, 20, 20,
+ "5GS 3GPP location information"),
+ EF_TRANSP_N(0x4F02, 0x02, "EF.5GSN3GPPLOCI", F_OPTIONAL, 20, 20,
+ "5GS non-3GPP location information"),
+ EF_LIN_FIX_N(0x4F03, 0x03, "EF.5GS3GPPNSC", F_OPTIONAL, 57, 57,
+ "5GS 3GPP Access NAS Security Context"),
+ EF_LIN_FIX_N(0x4F04, 0x04, "EF.5GSN3GPPNSC", F_OPTIONAL, 57, 57,
+ "5GS non-3GPP Access NAS Security Context"),
+ EF_TRANSP_N(0x4F05, 0x05, "EF.5GAUTHKEYS", F_OPTIONAL, 68, 68,
+ "5GS authentication keys"),
+ EF_TRANSP_N(0x4F06, 0x06, "EF.UAC_AIC", F_OPTIONAL, 4, 4,
+ "UAC Access Identities Configuration"),
+ EF_TRANSP_N(0x4F07, 0x07, "EF.SUCI_Calc_Info", F_OPTIONAL, 2, 64,
+ "Subscription Concealed Identifier Calculation Information"),
+ EF_LIN_FIX_N(0x4F08, 0x08, "EF.OPL5G", F_OPTIONAL, 10, 10,
+ "5GS Operator PLMN List"),
+ EF_TRANSP_N(0x4F09, 0x09, "EF.NSI", F_OPTIONAL, 1, 64,
+ "Network Specific Identifier"),
+ EF_TRANSP_N(0x4F0A, 0x0A, "EF.Routing_Indicator", F_OPTIONAL, 4, 4,
+ "Routing Indicator"),
+};
+
/* Annex E - TS 101 220 */
static const uint8_t adf_usim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02 };
@@ -375,6 +437,8 @@ struct osim_card_app_profile *osim_aprof_usim(void *ctx)
ARRAY_SIZE(usim_ef_in_df_mexe));
add_df_with_ef(uadf, 0x5F40, "DF.WLAN", usim_ef_in_df_wlan,
ARRAY_SIZE(usim_ef_in_df_wlan));
+ add_df_with_ef(uadf, 0x5FC0, "DF.5GS", usim_ef_in_df_5gs,
+ ARRAY_SIZE(usim_ef_in_df_5gs));
/* Home-NodeB (femtocell) */
add_df_with_ef(uadf, 0x5F50, "DF.HNB", usim_ef_in_df_hnb,
ARRAY_SIZE(usim_ef_in_df_hnb));
@@ -383,6 +447,10 @@ struct osim_card_app_profile *osim_aprof_usim(void *ctx)
ARRAY_SIZE(usim_ef_in_solsa));
/* OMA BCAST Smart Card Profile */
add_df_with_ef(uadf, 0x5F80, "DF.BCAST", NULL, 0);
+ add_df_with_ef(uadf, 0x5F90, "DF.ProSe", usim_ef_in_df_prose,
+ ARRAY_SIZE(usim_ef_in_df_prose));
+ add_df_with_ef(uadf, 0x5FA0, "DF.ACDC", usim_ef_in_df_acdc,
+ ARRAY_SIZE(usim_ef_in_df_acdc));
return aprof;
}
diff --git a/src/stats_statsd.c b/src/stats_statsd.c
index d4496677..99764e64 100644
--- a/src/stats_statsd.c
+++ b/src/stats_statsd.c
@@ -32,6 +32,7 @@
#include <string.h>
#include <stdint.h>
+#include <inttypes.h>
#include <errno.h>
#include <osmocom/core/utils.h>
@@ -100,15 +101,15 @@ static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
if (prefix) {
if (name1)
- fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s";
+ fmt = "%1$s.%2$s.%6$u.%3$s:%4$" PRId64 "|%5$s";
else
- fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
+ fmt = "%1$s.%2$0.0s%3$s:%4$" PRId64 "|%5$s";
} else {
prefix = "";
if (name1)
- fmt = "%1$s%2$s.%6$u.%3$s:%4$d|%5$s";
+ fmt = "%1$s%2$s.%6$u.%3$s:%4$" PRId64 "|%5$s";
else
- fmt = "%1$s%2$0.0s%3$s:%4$d|%5$s";
+ fmt = "%1$s%2$0.0s%3$s:%4$" PRId64 "|%5$s";
}
if (srep->agg_enabled) {
diff --git a/src/tdef.c b/src/tdef.c
index 71a33158..897a92fd 100644
--- a/src/tdef.c
+++ b/src/tdef.c
@@ -93,6 +93,17 @@ static unsigned long osmo_tdef_factor(enum osmo_tdef_unit a, enum osmo_tdef_unit
return 1;
switch (b) {
+ case OSMO_TDEF_US:
+ switch (a) {
+ case OSMO_TDEF_MS:
+ return 1000;
+ case OSMO_TDEF_S:
+ return 1000*1000;
+ case OSMO_TDEF_M:
+ return 60*1000*1000;
+ default:
+ return 0;
+ }
case OSMO_TDEF_MS:
switch (a) {
case OSMO_TDEF_S:
@@ -351,6 +362,7 @@ const struct value_string osmo_tdef_unit_names[] = {
{ OSMO_TDEF_MS, "ms" },
{ OSMO_TDEF_M, "m" },
{ OSMO_TDEF_CUSTOM, "custom-unit" },
+ { OSMO_TDEF_US, "us" },
{}
};
diff --git a/src/vty/command.c b/src/vty/command.c
index 7ea19712..b38be34e 100644
--- a/src/vty/command.c
+++ b/src/vty/command.c
@@ -773,8 +773,23 @@ static bool vty_command_is_common(const struct cmd_element *cmd);
/*
* Dump all nodes and commands associated with a given node as XML via a print_func_t.
+ *
+ * (gflag_mask, match = false) - print only those commands with non-matching flags.
+ * (gflag_mask, match = true) - print only those commands with matching flags.
+ *
+ * Some examples:
+ *
+ * Print all commands except deprecated and hidden (default mode):
+ * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false)
+ * Print only deprecated and hidden commands:
+ * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true)
+ * Print all commands except deprecated (expert mode):
+ * (CMD_ATTR_DEPRECATED, false)
+ * Print only hidden commands:
+ * (CMD_ATTR_HIDDEN, true)
*/
-static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline)
+static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline,
+ unsigned char gflag_mask, bool match)
{
int i, j;
int same_name_count;
@@ -797,9 +812,9 @@ static int vty_dump_nodes(print_func_t print_func, void *data, const char *newli
const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
if (!vty_command_is_common(elem))
continue;
- if (elem->attr & CMD_ATTR_DEPRECATED)
+ if (!match && (elem->attr & gflag_mask) != 0x00)
continue;
- if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN))
+ if (match && (elem->attr & gflag_mask) == 0x00)
continue;
vty_dump_element(elem, print_func, data, newline);
}
@@ -835,9 +850,9 @@ static int vty_dump_nodes(print_func_t print_func, void *data, const char *newli
const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
if (vty_command_is_common(elem))
continue;
- if (elem->attr & CMD_ATTR_DEPRECATED)
+ if (!match && (elem->attr & gflag_mask) != 0x00)
continue;
- if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN))
+ if (match && (elem->attr & gflag_mask) == 0x00)
continue;
vty_dump_element(elem, print_func, data, newline);
}
@@ -863,7 +878,10 @@ static int print_func_vty(void *data, const char *format, ...)
static int vty_dump_xml_ref_to_vty(struct vty *vty)
{
- return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE);
+ unsigned char gflag_mask = CMD_ATTR_DEPRECATED;
+ if (!vty->expert_mode)
+ gflag_mask |= CMD_ATTR_HIDDEN;
+ return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false);
}
static int print_func_stream(void *data, const char *format, ...)
@@ -879,12 +897,14 @@ static int print_func_stream(void *data, const char *format, ...)
const struct value_string vty_ref_gen_mode_names[] = {
{ VTY_REF_GEN_MODE_DEFAULT, "default" },
{ VTY_REF_GEN_MODE_EXPERT, "expert" },
+ { VTY_REF_GEN_MODE_HIDDEN, "hidden" },
{ 0, NULL }
};
const struct value_string vty_ref_gen_mode_desc[] = {
{ VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
{ VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
+ { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" },
{ 0, NULL }
};
@@ -895,17 +915,27 @@ const struct value_string vty_ref_gen_mode_desc[] = {
*/
int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
{
+ unsigned char gflag_mask;
+ bool match = false;
+
switch (mode) {
case VTY_REF_GEN_MODE_EXPERT:
- host.expert_mode = true;
+ /* All commands except deprecated */
+ gflag_mask = CMD_ATTR_DEPRECATED;
+ break;
+ case VTY_REF_GEN_MODE_HIDDEN:
+ /* Only hidden commands */
+ gflag_mask = CMD_ATTR_HIDDEN;
+ match = true;
break;
case VTY_REF_GEN_MODE_DEFAULT:
default:
- host.expert_mode = false;
+ /* All commands except deprecated and hidden */
+ gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
break;
}
- return vty_dump_nodes(print_func_stream, stream, "\n");
+ return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match);
}
/*! Print the XML reference of all VTY nodes to the given stream.
@@ -2051,7 +2081,7 @@ cmd_describe_command_real(vector vline, struct vty *vty, int *status)
if (cmd_element->attr & CMD_ATTR_DEPRECATED)
continue;
- if (!host.expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
+ if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
continue;
strvec = cmd_element->strvec;
@@ -2923,7 +2953,7 @@ DEFUN(enable, config_enable_cmd,
else
vty->node = AUTH_ENABLE_NODE;
- host.expert_mode = argc > 0;
+ vty->expert_mode = argc > 0;
return CMD_SUCCESS;
}
@@ -2935,7 +2965,7 @@ DEFUN(disable,
if (vty->node == ENABLE_NODE)
vty->node = VIEW_NODE;
- host.expert_mode = false;
+ vty->expert_mode = false;
return CMD_SUCCESS;
}
@@ -3123,7 +3153,7 @@ gDEFUN(show_vty_attr, show_vty_attr_cmd,
}
/* Compose flag bit-mask for all commands within the given node */
-static unsigned int node_flag_mask(const struct cmd_node *cnode)
+static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
{
unsigned int flag_mask = 0x00;
unsigned int f, i;
@@ -3137,7 +3167,7 @@ static unsigned int node_flag_mask(const struct cmd_node *cnode)
continue;
if (cmd->attr & CMD_ATTR_DEPRECATED)
continue;
- if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
+ if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
continue;
if (~cmd->usrattr & ((unsigned)1 << f))
continue;
@@ -3221,14 +3251,14 @@ gDEFUN(config_list, config_list_cmd,
struct cmd_element *cmd;
if (argc > 0)
- flag_mask = node_flag_mask(cnode);
+ flag_mask = node_flag_mask(cnode, vty->expert_mode);
for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
continue;
if (cmd->attr & CMD_ATTR_DEPRECATED)
continue;
- if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
+ if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
continue;
if (!argc)
vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
@@ -4302,7 +4332,6 @@ void cmd_init(int terminal)
host.lines = -1;
host.motd = default_motd;
host.motdfile = NULL;
- host.expert_mode = false;
/* Install top nodes. */
install_node_bare(&view_node, NULL);
diff --git a/src/vty/cpu_sched_vty.c b/src/vty/cpu_sched_vty.c
index 0b72abb5..4ccc6274 100644
--- a/src/vty/cpu_sched_vty.c
+++ b/src/vty/cpu_sched_vty.c
@@ -371,7 +371,7 @@ static int my_sched_setaffinity(enum sched_vty_thread_id tid_type, pid_t pid,
}
-DEFUN(cfg_sched_cpu_affinity, cfg_sched_cpu_affinity_cmd,
+DEFUN_ATTR(cfg_sched_cpu_affinity, cfg_sched_cpu_affinity_cmd,
"cpu-affinity (self|all|<0-4294967295>|THREADNAME) CPUHEXMASK [delay]",
"Set CPU affinity mask on a (group of) thread(s)\n"
"Set CPU affinity mask on thread running the VTY\n"
@@ -379,7 +379,8 @@ DEFUN(cfg_sched_cpu_affinity, cfg_sched_cpu_affinity_cmd,
"Set CPU affinity mask on a thread with specified PID\n"
"Set CPU affinity mask on a thread with specified thread name\n"
"CPU affinity mask\n"
- "If set, delay applying the affinity mask now and let the app handle it at a later point\n")
+ "If set, delay applying the affinity mask now and let the app handle it at a later point\n",
+ CMD_ATTR_IMMEDIATE)
{
const char* str_who = argv[0];
const char *str_mask = argv[1];
@@ -476,11 +477,12 @@ static int set_sched_rr(unsigned int prio)
return 0;
}
-DEFUN(cfg_sched_policy, cfg_sched_policy_cmd,
+DEFUN_ATTR(cfg_sched_policy, cfg_sched_policy_cmd,
"policy rr <1-32>",
"Set the scheduling policy to use for the process\n"
"Use the SCHED_RR real-time scheduling algorithm\n"
- "Set the SCHED_RR real-time priority\n")
+ "Set the SCHED_RR real-time priority\n",
+ CMD_ATTR_IMMEDIATE)
{
sched_vty_opts->sched_rr_prio = atoi(argv[0]);
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index d2ae6f6c..5a596397 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -327,6 +327,203 @@ static void test_lai_encode_decode(void)
}
}
+static void dump_cm3(struct gsm48_classmark3 *cm3)
+{
+ printf("mult_band_supp=%02x\n", cm3->mult_band_supp);
+ printf("a5_bits=%02x\n", cm3->a5_bits);
+ printf("assoc_radio_cap_1=%02x\n", cm3->assoc_radio_cap_1);
+ printf("assoc_radio_cap_2=%02x\n", cm3->assoc_radio_cap_2);
+ printf("\n");
+ printf("r_support.present=%u\n", cm3->r_support.present);
+ printf("r_support.r_gsm_assoc_radio_cap=%02x\n",
+ cm3->r_support.r_gsm_assoc_radio_cap);
+ printf("\n");
+ printf("hscsd_mult_slot_cap.present=%u\n",
+ cm3->hscsd_mult_slot_cap.present);
+ printf("hscsd_mult_slot_cap.mslot_class=%02x\n",
+ cm3->hscsd_mult_slot_cap.mslot_class);
+ printf("\n");
+ printf("ucs2_treatment=%u\n", cm3->ucs2_treatment);
+ printf("extended_meas_cap=%u\n", cm3->extended_meas_cap);
+ printf("\n");
+ printf("ms_meas_cap.present=%u\n", cm3->ms_meas_cap.present);
+ printf("ms_meas_cap.sms_value=%02x\n", cm3->ms_meas_cap.sms_value);
+ printf("ms_meas_cap.sm_value=%02x\n", cm3->ms_meas_cap.sm_value);
+ printf("\n");
+ printf("ms_pos_method_cap.present=%u\n",
+ cm3->ms_pos_method_cap.present);
+ printf("ms_pos_method_cap.method=%02x\n",
+ cm3->ms_pos_method_cap.method);
+ printf("\n");
+ printf("ecsd_multislot_cap.present=%u\n",
+ cm3->ecsd_multislot_cap.present);
+ printf("ecsd_multislot_cap.mslot_class=%02x\n",
+ cm3->ecsd_multislot_cap.mslot_class);
+ printf("\n");
+ printf("psk8_struct.present=%u\n", cm3->psk8_struct.present);
+ printf("psk8_struct.mod_cap=%u\n", cm3->psk8_struct.mod_cap);
+ printf("psk8_struct.rf_pwr_cap_1.present=%u\n",
+ cm3->psk8_struct.rf_pwr_cap_1.present);
+ printf("psk8_struct.rf_pwr_cap_1.value=%02x\n",
+ cm3->psk8_struct.rf_pwr_cap_1.value);
+ printf("psk8_struct.rf_pwr_cap_2.present=%u\n",
+ cm3->psk8_struct.rf_pwr_cap_2.present);
+ printf("psk8_struct.rf_pwr_cap_2.value=%02x\n",
+ cm3->psk8_struct.rf_pwr_cap_2.value);
+ printf("\n");
+ printf("gsm_400_bands_supp.present=%u\n",
+ cm3->gsm_400_bands_supp.present);
+ printf("gsm_400_bands_supp.value=%02x\n",
+ cm3->gsm_400_bands_supp.value);
+ printf("gsm_400_bands_supp.assoc_radio_cap=%02x\n",
+ cm3->gsm_400_bands_supp.assoc_radio_cap);
+ printf("\n");
+ printf("gsm_850_assoc_radio_cap.present=%u\n",
+ cm3->gsm_850_assoc_radio_cap.present);
+ printf("gsm_850_assoc_radio_cap.value=%02x\n",
+ cm3->gsm_850_assoc_radio_cap.value);
+ printf("\n");
+ printf("gsm_1900_assoc_radio_cap.present=%u\n",
+ cm3->gsm_1900_assoc_radio_cap.present);
+ printf("gsm_1900_assoc_radio_cap.value=%02x\n",
+ cm3->gsm_1900_assoc_radio_cap.value);
+ printf("\n");
+ printf("umts_fdd_rat_cap=%u\n", cm3->umts_fdd_rat_cap);
+ printf("umts_tdd_rat_cap=%u\n", cm3->umts_tdd_rat_cap);
+ printf("cdma200_rat_cap=%u\n", cm3->cdma200_rat_cap);
+ printf("\n");
+ printf("dtm_gprs_multislot_cap.present=%u\n",
+ cm3->dtm_gprs_multislot_cap.present);
+ printf("dtm_gprs_multislot_cap.mslot_class=%02x\n",
+ cm3->dtm_gprs_multislot_cap.mslot_class);
+ printf("dtm_gprs_multislot_cap.single_slot_dtm=%u\n",
+ cm3->dtm_gprs_multislot_cap.single_slot_dtm);
+ printf("dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=%u\n",
+ cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present);
+ printf("dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=%02x\n",
+ cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class);
+ printf("\n");
+ printf("single_band_supp.present=%u\n", cm3->single_band_supp.present);
+ printf("single_band_supp.value=%u\n", cm3->single_band_supp.value);
+ printf("\n");
+ printf("gsm_750_assoc_radio_cap.present=%u\n",
+ cm3->gsm_750_assoc_radio_cap.present);
+ printf("gsm_750_assoc_radio_cap.value=%02x\n",
+ cm3->gsm_750_assoc_radio_cap.value);
+ printf("\n");
+ printf("umts_1_28_mcps_tdd_rat_cap=%u\n",
+ cm3->umts_1_28_mcps_tdd_rat_cap);
+ printf("geran_feature_package=%u\n", cm3->geran_feature_package);
+ printf("\n");
+ printf("extended_dtm_gprs_multislot_cap.present=%u\n",
+ cm3->extended_dtm_gprs_multislot_cap.present);
+ printf("extended_dtm_gprs_multislot_cap.mslot_class=%02x\n",
+ cm3->extended_dtm_gprs_multislot_cap.mslot_class);
+ printf
+ ("extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=%u\n",
+ cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.present);
+ printf
+ ("extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=%02x\n",
+ cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.mslot_class);
+ printf("\n");
+ printf("high_multislot_cap.present=%u\n",
+ cm3->high_multislot_cap.present);
+ printf("high_multislot_cap.value=%02x\n",
+ cm3->high_multislot_cap.value);
+ printf("\n");
+ printf("geran_feature_package_2=%u\n", cm3->geran_feature_package_2);
+ printf("gmsk_multislot_power_prof=%02x\n",
+ cm3->gmsk_multislot_power_prof);
+ printf("psk8_multislot_power_prof=%02x\n",
+ cm3->psk8_multislot_power_prof);
+ printf("\n");
+ printf("t_gsm_400_bands_supp.present=%u\n",
+ cm3->t_gsm_400_bands_supp.present);
+ printf("t_gsm_400_bands_supp.value=%02x\n",
+ cm3->t_gsm_400_bands_supp.value);
+ printf("t_gsm_400_bands_supp.assoc_radio_cap=%02x\n",
+ cm3->t_gsm_400_bands_supp.assoc_radio_cap);
+ printf("\n");
+ printf("dl_advanced_rx_perf=%02x\n", cm3->dl_advanced_rx_perf);
+ printf("dtm_enhancements_cap=%u\n", cm3->dtm_enhancements_cap);
+ printf("\n");
+ printf("dtm_gprs_high_multislot_cap.present=%u\n",
+ cm3->dtm_gprs_high_multislot_cap.present);
+ printf("dtm_gprs_high_multislot_cap.mslot_class=%02x\n",
+ cm3->dtm_gprs_high_multislot_cap.mslot_class);
+ printf("dtm_gprs_high_multislot_cap.offset_required=%u\n",
+ cm3->dtm_gprs_high_multislot_cap.offset_required);
+ printf
+ ("dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.present=%u\n",
+ cm3->dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.
+ present);
+ printf
+ ("dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.mslot_class=%02x\n",
+ cm3->dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.
+ mslot_class);
+ printf("\n");
+ printf("repeated_acch_capability=%u\n", cm3->repeated_acch_capability);
+ printf("\n");
+ printf("gsm_710_assoc_radio_cap.present=%u\n",
+ cm3->gsm_710_assoc_radio_cap.present);
+ printf("gsm_710_assoc_radio_cap.value=%02x\n",
+ cm3->gsm_710_assoc_radio_cap.value);
+ printf("\n");
+ printf("t_gsm_810_assoc_radio_cap.present=%u\n",
+ cm3->t_gsm_810_assoc_radio_cap.present);
+ printf("t_gsm_810_assoc_radio_cap.value=%02x\n",
+ cm3->t_gsm_810_assoc_radio_cap.value);
+ printf("\n");
+ printf("ciphering_mode_setting_cap=%u\n",
+ cm3->ciphering_mode_setting_cap);
+ printf("add_pos_cap=%u\n", cm3->add_pos_cap);
+ printf("e_utra_fdd_supp=%u\n", cm3->e_utra_fdd_supp);
+ printf("e_utra_tdd_supp=%u\n", cm3->e_utra_tdd_supp);
+ printf("e_utra_meas_rep_supp=%u\n", cm3->e_utra_meas_rep_supp);
+ printf("prio_resel_supp=%u\n", cm3->prio_resel_supp);
+ printf("utra_csg_cells_rep=%u\n", cm3->utra_csg_cells_rep);
+ printf("vamos_level=%02x\n", cm3->vamos_level);
+ printf("tighter_capability=%02x\n", cm3->tighter_capability);
+ printf("sel_ciph_dl_sacch=%u\n", cm3->sel_ciph_dl_sacch);
+ printf("cs_ps_srvcc_geran_utra=%02x\n", cm3->cs_ps_srvcc_geran_utra);
+ printf("cs_ps_srvcc_geran_eutra=%02x\n", cm3->cs_ps_srvcc_geran_eutra);
+ printf("geran_net_sharing=%u\n", cm3->geran_net_sharing);
+ printf("e_utra_wb_rsrq_meas_supp=%u\n", cm3->e_utra_wb_rsrq_meas_supp);
+ printf("er_band_support=%u\n", cm3->er_band_support);
+ printf("utra_mult_band_ind_supp=%u\n", cm3->utra_mult_band_ind_supp);
+ printf("e_utra_mult_band_ind_supp=%u\n",
+ cm3->e_utra_mult_band_ind_supp);
+ printf("extended_tsc_set_cap_supp=%u\n",
+ cm3->extended_tsc_set_cap_supp);
+ printf("extended_earfcn_val_range=%u\n",
+ cm3->extended_earfcn_val_range);
+}
+
+static void test_decode_classmark3(void)
+{
+ struct gsm48_classmark3 cm3;
+ const uint8_t cm3_1[] = { 0x60, 0x14, 0x04, 0x2f, 0x65, 0x00, 0x20, 0x03, 0x40, 0x4a };
+ const uint8_t cm3_2[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
+ const uint8_t cm3_3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa};
+
+ printf("=====cm3_1=====\n");
+ gsm48_decode_classmark3(&cm3, cm3_1, sizeof(cm3_1));
+ dump_cm3(&cm3);
+ printf("\n");
+
+ printf("=====cm3_2=====\n");
+ gsm48_decode_classmark3(&cm3, cm3_2, sizeof(cm3_2));
+ dump_cm3(&cm3);
+ printf("\n");
+
+ printf("=====cm3_3=====\n");
+ gsm48_decode_classmark3(&cm3, cm3_3, sizeof(cm3_3));
+ dump_cm3(&cm3);
+ printf("\n");
+}
+
static void test_mid_from_tmsi(void)
{
static const uint8_t res[] = { 0x17, 0x05, 0xf4, 0xaa, 0xbb, 0xcc, 0xdd };
@@ -1550,6 +1747,7 @@ int main(int argc, char **argv)
test_bcd_number_encode_decode();
test_ra_cap();
test_lai_encode_decode();
+ test_decode_classmark3();
test_si_range_helpers();
test_arfcn_filter();
diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index 3e6ae1f4..52c4ea72 100644
--- a/tests/gsm0408/gsm0408_test.ok
+++ b/tests/gsm0408/gsm0408_test.ok
@@ -385,6 +385,348 @@ RA test...passed
Encoded 21 63 54 00 17
gsm48_decode_lai2() gives 123-456-23 (3-digit MNC)
passed
+=====cm3_1=====
+mult_band_supp=06
+a5_bits=00
+assoc_radio_cap_1=04
+assoc_radio_cap_2=01
+
+r_support.present=0
+r_support.r_gsm_assoc_radio_cap=00
+
+hscsd_mult_slot_cap.present=0
+hscsd_mult_slot_cap.mslot_class=00
+
+ucs2_treatment=0
+extended_meas_cap=0
+
+ms_meas_cap.present=0
+ms_meas_cap.sms_value=00
+ms_meas_cap.sm_value=00
+
+ms_pos_method_cap.present=1
+ms_pos_method_cap.method=01
+
+ecsd_multislot_cap.present=0
+ecsd_multislot_cap.mslot_class=00
+
+psk8_struct.present=1
+psk8_struct.mod_cap=1
+psk8_struct.rf_pwr_cap_1.present=1
+psk8_struct.rf_pwr_cap_1.value=02
+psk8_struct.rf_pwr_cap_2.present=1
+psk8_struct.rf_pwr_cap_2.value=02
+
+gsm_400_bands_supp.present=0
+gsm_400_bands_supp.value=00
+gsm_400_bands_supp.assoc_radio_cap=00
+
+gsm_850_assoc_radio_cap.present=1
+gsm_850_assoc_radio_cap.value=04
+
+gsm_1900_assoc_radio_cap.present=0
+gsm_1900_assoc_radio_cap.value=00
+
+umts_fdd_rat_cap=0
+umts_tdd_rat_cap=0
+cdma200_rat_cap=0
+
+dtm_gprs_multislot_cap.present=0
+dtm_gprs_multislot_cap.mslot_class=00
+dtm_gprs_multislot_cap.single_slot_dtm=0
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=0
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=00
+
+single_band_supp.present=0
+single_band_supp.value=0
+
+gsm_750_assoc_radio_cap.present=0
+gsm_750_assoc_radio_cap.value=00
+
+umts_1_28_mcps_tdd_rat_cap=0
+geran_feature_package=1
+
+extended_dtm_gprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.mslot_class=00
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=00
+
+high_multislot_cap.present=0
+high_multislot_cap.value=00
+
+geran_feature_package_2=0
+gmsk_multislot_power_prof=00
+psk8_multislot_power_prof=00
+
+t_gsm_400_bands_supp.present=0
+t_gsm_400_bands_supp.value=00
+t_gsm_400_bands_supp.assoc_radio_cap=00
+
+dl_advanced_rx_perf=01
+dtm_enhancements_cap=1
+
+dtm_gprs_high_multislot_cap.present=0
+dtm_gprs_high_multislot_cap.mslot_class=00
+dtm_gprs_high_multislot_cap.offset_required=0
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.present=0
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.mslot_class=00
+
+repeated_acch_capability=1
+
+gsm_710_assoc_radio_cap.present=0
+gsm_710_assoc_radio_cap.value=00
+
+t_gsm_810_assoc_radio_cap.present=0
+t_gsm_810_assoc_radio_cap.value=00
+
+ciphering_mode_setting_cap=0
+add_pos_cap=0
+e_utra_fdd_supp=0
+e_utra_tdd_supp=0
+e_utra_meas_rep_supp=0
+prio_resel_supp=1
+utra_csg_cells_rep=0
+vamos_level=01
+tighter_capability=01
+sel_ciph_dl_sacch=0
+cs_ps_srvcc_geran_utra=00
+cs_ps_srvcc_geran_eutra=00
+geran_net_sharing=0
+e_utra_wb_rsrq_meas_supp=0
+er_band_support=0
+utra_mult_band_ind_supp=0
+e_utra_mult_band_ind_supp=0
+extended_tsc_set_cap_supp=0
+extended_earfcn_val_range=0
+
+=====cm3_2=====
+mult_band_supp=05
+a5_bits=05
+assoc_radio_cap_1=05
+assoc_radio_cap_2=05
+
+r_support.present=0
+r_support.r_gsm_assoc_radio_cap=00
+
+hscsd_mult_slot_cap.present=1
+hscsd_mult_slot_cap.mslot_class=0a
+
+ucs2_treatment=1
+extended_meas_cap=0
+
+ms_meas_cap.present=1
+ms_meas_cap.sms_value=05
+ms_meas_cap.sm_value=05
+
+ms_pos_method_cap.present=0
+ms_pos_method_cap.method=00
+
+ecsd_multislot_cap.present=1
+ecsd_multislot_cap.mslot_class=0a
+
+psk8_struct.present=1
+psk8_struct.mod_cap=0
+psk8_struct.rf_pwr_cap_1.present=1
+psk8_struct.rf_pwr_cap_1.value=01
+psk8_struct.rf_pwr_cap_2.present=0
+psk8_struct.rf_pwr_cap_2.value=00
+
+gsm_400_bands_supp.present=1
+gsm_400_bands_supp.value=01
+gsm_400_bands_supp.assoc_radio_cap=05
+
+gsm_850_assoc_radio_cap.present=0
+gsm_850_assoc_radio_cap.value=00
+
+gsm_1900_assoc_radio_cap.present=1
+gsm_1900_assoc_radio_cap.value=05
+
+umts_fdd_rat_cap=0
+umts_tdd_rat_cap=1
+cdma200_rat_cap=0
+
+dtm_gprs_multislot_cap.present=1
+dtm_gprs_multislot_cap.mslot_class=01
+dtm_gprs_multislot_cap.single_slot_dtm=0
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=1
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=01
+
+single_band_supp.present=0
+single_band_supp.value=0
+
+gsm_750_assoc_radio_cap.present=1
+gsm_750_assoc_radio_cap.value=05
+
+umts_1_28_mcps_tdd_rat_cap=0
+geran_feature_package=1
+
+extended_dtm_gprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.mslot_class=00
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=00
+
+high_multislot_cap.present=1
+high_multislot_cap.value=01
+
+geran_feature_package_2=1
+gmsk_multislot_power_prof=01
+psk8_multislot_power_prof=01
+
+t_gsm_400_bands_supp.present=0
+t_gsm_400_bands_supp.value=00
+t_gsm_400_bands_supp.assoc_radio_cap=00
+
+dl_advanced_rx_perf=01
+dtm_enhancements_cap=0
+
+dtm_gprs_high_multislot_cap.present=1
+dtm_gprs_high_multislot_cap.mslot_class=02
+dtm_gprs_high_multislot_cap.offset_required=1
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.present=0
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.mslot_class=00
+
+repeated_acch_capability=1
+
+gsm_710_assoc_radio_cap.present=0
+gsm_710_assoc_radio_cap.value=00
+
+t_gsm_810_assoc_radio_cap.present=1
+t_gsm_810_assoc_radio_cap.value=05
+
+ciphering_mode_setting_cap=0
+add_pos_cap=1
+e_utra_fdd_supp=0
+e_utra_tdd_supp=0
+e_utra_meas_rep_supp=0
+prio_resel_supp=0
+utra_csg_cells_rep=0
+vamos_level=00
+tighter_capability=00
+sel_ciph_dl_sacch=0
+cs_ps_srvcc_geran_utra=00
+cs_ps_srvcc_geran_eutra=00
+geran_net_sharing=0
+e_utra_wb_rsrq_meas_supp=0
+er_band_support=0
+utra_mult_band_ind_supp=0
+e_utra_mult_band_ind_supp=0
+extended_tsc_set_cap_supp=0
+extended_earfcn_val_range=0
+
+=====cm3_3=====
+mult_band_supp=02
+a5_bits=0a
+assoc_radio_cap_1=0a
+assoc_radio_cap_2=00
+
+r_support.present=1
+r_support.r_gsm_assoc_radio_cap=02
+
+hscsd_mult_slot_cap.present=1
+hscsd_mult_slot_cap.mslot_class=0a
+
+ucs2_treatment=1
+extended_meas_cap=0
+
+ms_meas_cap.present=1
+ms_meas_cap.sms_value=05
+ms_meas_cap.sm_value=05
+
+ms_pos_method_cap.present=0
+ms_pos_method_cap.method=00
+
+ecsd_multislot_cap.present=1
+ecsd_multislot_cap.mslot_class=0a
+
+psk8_struct.present=1
+psk8_struct.mod_cap=0
+psk8_struct.rf_pwr_cap_1.present=1
+psk8_struct.rf_pwr_cap_1.value=01
+psk8_struct.rf_pwr_cap_2.present=0
+psk8_struct.rf_pwr_cap_2.value=00
+
+gsm_400_bands_supp.present=1
+gsm_400_bands_supp.value=01
+gsm_400_bands_supp.assoc_radio_cap=05
+
+gsm_850_assoc_radio_cap.present=0
+gsm_850_assoc_radio_cap.value=00
+
+gsm_1900_assoc_radio_cap.present=1
+gsm_1900_assoc_radio_cap.value=05
+
+umts_fdd_rat_cap=0
+umts_tdd_rat_cap=1
+cdma200_rat_cap=0
+
+dtm_gprs_multislot_cap.present=1
+dtm_gprs_multislot_cap.mslot_class=01
+dtm_gprs_multislot_cap.single_slot_dtm=0
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=1
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=01
+
+single_band_supp.present=0
+single_band_supp.value=0
+
+gsm_750_assoc_radio_cap.present=1
+gsm_750_assoc_radio_cap.value=05
+
+umts_1_28_mcps_tdd_rat_cap=0
+geran_feature_package=1
+
+extended_dtm_gprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.mslot_class=00
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=00
+
+high_multislot_cap.present=1
+high_multislot_cap.value=01
+
+geran_feature_package_2=1
+gmsk_multislot_power_prof=01
+psk8_multislot_power_prof=01
+
+t_gsm_400_bands_supp.present=0
+t_gsm_400_bands_supp.value=00
+t_gsm_400_bands_supp.assoc_radio_cap=00
+
+dl_advanced_rx_perf=01
+dtm_enhancements_cap=0
+
+dtm_gprs_high_multislot_cap.present=1
+dtm_gprs_high_multislot_cap.mslot_class=02
+dtm_gprs_high_multislot_cap.offset_required=1
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.present=0
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.mslot_class=00
+
+repeated_acch_capability=1
+
+gsm_710_assoc_radio_cap.present=0
+gsm_710_assoc_radio_cap.value=00
+
+t_gsm_810_assoc_radio_cap.present=1
+t_gsm_810_assoc_radio_cap.value=04
+
+ciphering_mode_setting_cap=0
+add_pos_cap=0
+e_utra_fdd_supp=0
+e_utra_tdd_supp=0
+e_utra_meas_rep_supp=0
+prio_resel_supp=0
+utra_csg_cells_rep=0
+vamos_level=00
+tighter_capability=00
+sel_ciph_dl_sacch=0
+cs_ps_srvcc_geran_utra=00
+cs_ps_srvcc_geran_eutra=00
+geran_net_sharing=0
+e_utra_wb_rsrq_meas_supp=0
+er_band_support=0
+utra_mult_band_ind_supp=0
+e_utra_mult_band_ind_supp=0
+extended_tsc_set_cap_supp=0
+extended_earfcn_val_range=0
+
Element is: 2 => freqs[i] = 121
Element is: 2 => freqs[i] = 1
Element is: 0 => freqs[i] = 68
diff --git a/tests/tdef/tdef_test.c b/tests/tdef/tdef_test.c
index 9c0808ea..ede3082e 100644
--- a/tests/tdef/tdef_test.c
+++ b/tests/tdef/tdef_test.c
@@ -38,7 +38,7 @@ static void *ctx = NULL;
static struct osmo_tdef tdefs[] = {
{ .T=1, .default_val=100, .desc="100s" },
{ .T=2, .default_val=100, .unit=OSMO_TDEF_MS, .desc="100ms" },
- { .T=3, .default_val=100, .unit=OSMO_TDEF_M, .desc="100m" },
+ { .T=3, .default_val=50, .unit=OSMO_TDEF_M, .desc="50m" },
{ .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="100 potatoes" },
{ .T=7, .default_val=50, .desc="Water Boiling Timeout", .min_val=20, .max_val=800 }, // default is .unit=OSMO_TDEF_S == 0
@@ -54,6 +54,7 @@ static struct osmo_tdef tdefs[] = {
{ .T=1006, .default_val=0, .unit=OSMO_TDEF_S, .desc="zero s" },
{ .T=1007, .default_val=0, .unit=OSMO_TDEF_M, .desc="zero m" },
{ .T=1008, .default_val=0, .unit=OSMO_TDEF_CUSTOM, .desc="zero" },
+ { .T=1009, .default_val=0, .unit=OSMO_TDEF_US, .desc="zero us" },
{ .T=0, .default_val=1, .unit=OSMO_TDEF_CUSTOM, .desc="zero" },
@@ -111,7 +112,7 @@ static void test_tdef_get(bool test_range)
for (i = 0; i < ARRAY_SIZE(tdefs)-1; i++) {
unsigned int T = tdefs[i].T;
print_tdef_info(T);
- for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_CUSTOM; as_unit++) {
+ for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_US; as_unit++) {
print_tdef_get_short(tdefs, T, as_unit);
}
}
@@ -122,7 +123,7 @@ static void test_tdef_get(bool test_range)
for (i = 0; i < ARRAY_SIZE(tdefs_range)-1; i++) {
unsigned int T = tdefs_range[i].T;
print_tdef_info(T);
- for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_CUSTOM; as_unit++) {
+ for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_US; as_unit++) {
print_tdef_get_short(tdefs_range, T, as_unit);
}
}
@@ -136,6 +137,7 @@ static void test_tdef_get_nonexisting()
print_tdef_get(tdefs, 5, OSMO_TDEF_MS);
print_tdef_get(tdefs, 5, OSMO_TDEF_M);
print_tdef_get(tdefs, 5, OSMO_TDEF_CUSTOM);
+ print_tdef_get(tdefs, 5, OSMO_TDEF_US);
}
static void test_tdef_set_and_get()
@@ -152,6 +154,7 @@ static void test_tdef_set_and_get()
print_tdef_get_short(tdefs, 7, OSMO_TDEF_S);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_M);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM);
+ print_tdef_get_short(tdefs, 7, OSMO_TDEF_US);
printf("setting 7 = 420\n");
OSMO_ASSERT(osmo_tdef_set(tdefs, 7, 420, OSMO_TDEF_S) == 0);
@@ -160,6 +163,7 @@ static void test_tdef_set_and_get()
print_tdef_get_short(tdefs, 7, OSMO_TDEF_S);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_M);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM);
+ print_tdef_get_short(tdefs, 7, OSMO_TDEF_US);
printf("setting 7 = 10 (ERANGE)\n");
OSMO_ASSERT(!osmo_tdef_val_in_range(t, 10));
@@ -169,6 +173,7 @@ static void test_tdef_set_and_get()
print_tdef_get_short(tdefs, 7, OSMO_TDEF_S);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_M);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM);
+ print_tdef_get_short(tdefs, 7, OSMO_TDEF_US);
printf("setting 7 = 900 (ERANGE)\n");
OSMO_ASSERT(!osmo_tdef_val_in_range(t, 900));
@@ -178,6 +183,7 @@ static void test_tdef_set_and_get()
print_tdef_get_short(tdefs, 7, OSMO_TDEF_S);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_M);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM);
+ print_tdef_get_short(tdefs, 7, OSMO_TDEF_US);
printf("setting 23 = 50 (EEXIST)\n");
OSMO_ASSERT(osmo_tdef_set(tdefs, 23, 50, OSMO_TDEF_S) == -EEXIST);
diff --git a/tests/tdef/tdef_test.ok b/tests/tdef/tdef_test.ok
index 3c4a0930..4c97dabb 100644
--- a/tests/tdef/tdef_test.ok
+++ b/tests/tdef/tdef_test.ok
@@ -5,92 +5,116 @@ osmo_tdef_get(1, s) = 100
osmo_tdef_get(1, ms) = 100000
osmo_tdef_get(1, m) = 2
osmo_tdef_get(1, custom-unit) = 100
+osmo_tdef_get(1, us) = 100000000
T2=100ms
osmo_tdef_get(2, s) = 1
osmo_tdef_get(2, ms) = 100
osmo_tdef_get(2, m) = 1
osmo_tdef_get(2, custom-unit) = 100
-T3=100m
-osmo_tdef_get(3, s) = 6000
-osmo_tdef_get(3, ms) = 6000000
-osmo_tdef_get(3, m) = 100
-osmo_tdef_get(3, custom-unit) = 100
+osmo_tdef_get(2, us) = 100000
+T3=50m
+osmo_tdef_get(3, s) = 3000
+osmo_tdef_get(3, ms) = 3000000
+osmo_tdef_get(3, m) = 50
+osmo_tdef_get(3, custom-unit) = 50
+osmo_tdef_get(3, us) = 3000000000
T4=100custom-unit
osmo_tdef_get(4, s) = 100
osmo_tdef_get(4, ms) = 100
osmo_tdef_get(4, m) = 100
osmo_tdef_get(4, custom-unit) = 100
+osmo_tdef_get(4, us) = 100
T7=50s
osmo_tdef_get(7, s) = 50
osmo_tdef_get(7, ms) = 50000
osmo_tdef_get(7, m) = 1
osmo_tdef_get(7, custom-unit) = 50
+osmo_tdef_get(7, us) = 50000000
T8=300s
osmo_tdef_get(8, s) = 300
osmo_tdef_get(8, ms) = 300000
osmo_tdef_get(8, m) = 5
osmo_tdef_get(8, custom-unit) = 300
+osmo_tdef_get(8, us) = 300000000
T9=5m
osmo_tdef_get(9, s) = 300
osmo_tdef_get(9, ms) = 300000
osmo_tdef_get(9, m) = 5
osmo_tdef_get(9, custom-unit) = 5
+osmo_tdef_get(9, us) = 300000000
T10=20m
osmo_tdef_get(10, s) = 1200
osmo_tdef_get(10, ms) = 1200000
osmo_tdef_get(10, m) = 20
osmo_tdef_get(10, custom-unit) = 20
+osmo_tdef_get(10, us) = 1200000000
T1000=2000ms
osmo_tdef_get(1000, s) = 2
osmo_tdef_get(1000, ms) = 2000
osmo_tdef_get(1000, m) = 1
osmo_tdef_get(1000, custom-unit) = 2000
+osmo_tdef_get(1000, us) = 2000000
T1001=60000ms
osmo_tdef_get(1001, s) = 60
osmo_tdef_get(1001, ms) = 60000
osmo_tdef_get(1001, m) = 1
osmo_tdef_get(1001, custom-unit) = 60000
+osmo_tdef_get(1001, us) = 60000000
T1004=1ms
osmo_tdef_get(1004, s) = 1
osmo_tdef_get(1004, ms) = 1
osmo_tdef_get(1004, m) = 1
osmo_tdef_get(1004, custom-unit) = 1
+osmo_tdef_get(1004, us) = 1000
T1005=0ms
osmo_tdef_get(1005, s) = 0
osmo_tdef_get(1005, ms) = 0
osmo_tdef_get(1005, m) = 0
osmo_tdef_get(1005, custom-unit) = 0
+osmo_tdef_get(1005, us) = 0
T1006=0s
osmo_tdef_get(1006, s) = 0
osmo_tdef_get(1006, ms) = 0
osmo_tdef_get(1006, m) = 0
osmo_tdef_get(1006, custom-unit) = 0
+osmo_tdef_get(1006, us) = 0
T1007=0m
osmo_tdef_get(1007, s) = 0
osmo_tdef_get(1007, ms) = 0
osmo_tdef_get(1007, m) = 0
osmo_tdef_get(1007, custom-unit) = 0
+osmo_tdef_get(1007, us) = 0
T1008=0custom-unit
osmo_tdef_get(1008, s) = 0
osmo_tdef_get(1008, ms) = 0
osmo_tdef_get(1008, m) = 0
osmo_tdef_get(1008, custom-unit) = 0
+osmo_tdef_get(1008, us) = 0
+T1009=0us
+osmo_tdef_get(1009, s) = 0
+osmo_tdef_get(1009, ms) = 0
+osmo_tdef_get(1009, m) = 0
+osmo_tdef_get(1009, custom-unit) = 0
+osmo_tdef_get(1009, us) = 0
T0=1custom-unit
osmo_tdef_get(0, s) = 1
osmo_tdef_get(0, ms) = 1
osmo_tdef_get(0, m) = 1
osmo_tdef_get(0, custom-unit) = 1
+osmo_tdef_get(0, us) = 1
T123=1s
osmo_tdef_get(123, s) = 1
osmo_tdef_get(123, ms) = 1000
osmo_tdef_get(123, m) = 1
osmo_tdef_get(123, custom-unit) = 1
+osmo_tdef_get(123, us) = 1000000
test_tdef_get_nonexisting()
osmo_tdef_get(tdefs, 5, s, 999) = 999
osmo_tdef_get(tdefs, 5, ms, 999) = 999
osmo_tdef_get(tdefs, 5, m, 999) = 999
osmo_tdef_get(tdefs, 5, custom-unit, 999) = 999
+osmo_tdef_get(tdefs, 5, us, 999) = 999
test_tdef_set_and_get()
setting 7 = 42
@@ -99,24 +123,28 @@ osmo_tdef_get(7, ms) = 42000
osmo_tdef_get(7, s) = 42
osmo_tdef_get(7, m) = 1
osmo_tdef_get(7, custom-unit) = 42
+osmo_tdef_get(7, us) = 42000000
setting 7 = 420
T7=420s(def=50)
osmo_tdef_get(7, ms) = 420000
osmo_tdef_get(7, s) = 420
osmo_tdef_get(7, m) = 7
osmo_tdef_get(7, custom-unit) = 420
+osmo_tdef_get(7, us) = 420000000
setting 7 = 10 (ERANGE)
T7=420s(def=50)
osmo_tdef_get(7, ms) = 420000
osmo_tdef_get(7, s) = 420
osmo_tdef_get(7, m) = 7
osmo_tdef_get(7, custom-unit) = 420
+osmo_tdef_get(7, us) = 420000000
setting 7 = 900 (ERANGE)
T7=420s(def=50)
osmo_tdef_get(7, ms) = 420000
osmo_tdef_get(7, s) = 420
osmo_tdef_get(7, m) = 7
osmo_tdef_get(7, custom-unit) = 420
+osmo_tdef_get(7, us) = 420000000
setting 23 = 50 (EEXIST)
resetting
T7=50s
@@ -126,7 +154,7 @@ test_tdef_state_timeout()
state=A T=0, no timeout
--> A (configured as T1 100 s) rc=0; state=A T=1, 100.000000 s remaining
--> B (configured as T2 100 ms) rc=0; state=B T=2, 1.000000 s remaining
- --> C (configured as T3 100 m) rc=0; state=C T=3, 6000.000000 s remaining
+ --> C (configured as T3 50 m) rc=0; state=C T=3, 3000.000000 s remaining
--> D (configured as T4 100 custom-unit) rc=0; state=D T=4, 100.000000 s remaining
--> G (configured as T7 50 s) rc=0; state=G T=7, 50.000000 s remaining
--> H (configured as T8 300 s) rc=0; state=H T=8, 300.000000 s remaining
@@ -155,5 +183,5 @@ state=A T=1, 76.954322 s remaining
- test disallowed transition:
--> Z (no timer configured for this state) rc=0; state=Z T=0, no timeout
--> B (configured as T2 100 ms) rc=-1; state=Z T=0, no timeout
- --> C (configured as T3 100 m) rc=-1; state=Z T=0, no timeout
+ --> C (configured as T3 50 m) rc=-1; state=Z T=0, no timeout
--> D (configured as T4 100 custom-unit) rc=-1; state=Z T=0, no timeout