Initial WIP code; doens'tdo anything useful yet
This commit is contained in:
commit
311afac0d2
|
@ -0,0 +1,40 @@
|
|||
config.*
|
||||
|
||||
.version
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
compile
|
||||
configure
|
||||
configure~
|
||||
depcomp
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
m4
|
||||
missing
|
||||
*.pc
|
||||
|
||||
.deps
|
||||
.libs
|
||||
|
||||
debian/.debhelper
|
||||
debian/autoreconf.*
|
||||
debian/osmo-isdntap
|
||||
debian/*.log
|
||||
debian/tmp
|
||||
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
|
||||
src/osmo-isdntap
|
||||
|
||||
tests/atconfig
|
||||
tests/atlocal
|
||||
tests/package.m4
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
tests/testsuite.dir
|
||||
tests/*/*_test
|
|
@ -0,0 +1,26 @@
|
|||
AUTOMAKE_OPTIONS = foreign dist-bzip2
|
||||
|
||||
SUBDIRS = \
|
||||
contrib \
|
||||
doc \
|
||||
src \
|
||||
tests \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
debian \
|
||||
doc \
|
||||
git-version-gen \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
|
@ -0,0 +1,121 @@
|
|||
AC_INIT([osmo-isdntap],
|
||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[openbsc@lists.osmocom.org])
|
||||
|
||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
|
||||
dnl libtool init
|
||||
LT_INIT
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.9])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl include release helper
|
||||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
dnl checks for programs
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_MKDIR_P
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
AC_MSG_WARN([You need to install pkg-config])
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[werror=$enableval], [werror="no"])
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([external_tests],
|
||||
AC_HELP_STRING([--enable-external-tests],
|
||||
[Include the VTY/CTRL tests in make check [default=no]]),
|
||||
[enable_ext_tests="$enableval"],[enable_ext_tests="no"])
|
||||
if test "x$enable_ext_tests" = "xyes" ; then
|
||||
AC_CHECK_PROG(PYTHON3_AVAIL,python3,yes)
|
||||
if test "x$PYTHON3_AVAIL" != "xyes" ; then
|
||||
AC_MSG_ERROR([Please install python3 to run the VTY/CTRL tests.])
|
||||
fi
|
||||
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
|
||||
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
|
||||
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
|
||||
fi
|
||||
fi
|
||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
AC_MSG_RESULT([$enable_ext_tests])
|
||||
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
|
||||
|
||||
|
||||
|
||||
# https://www.freedesktop.org/software/systemd/man/daemon.html
|
||||
AC_ARG_WITH([systemdsystemunitdir],
|
||||
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||
[with_systemdsystemunitdir=auto])
|
||||
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
|
||||
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
|
||||
|
||||
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
|
||||
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
|
||||
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
|
||||
with_systemdsystemunitdir=no],
|
||||
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
|
||||
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
|
||||
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
|
||||
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
|
||||
|
||||
AC_OUTPUT(
|
||||
Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
src/Makefile
|
||||
tests/Makefile
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
SUBDIRS = systemd
|
|
@ -0,0 +1,6 @@
|
|||
EXTRA_DIST = osmo-isdntap.service
|
||||
|
||||
if HAVE_SYSTEMD
|
||||
systemdsystemunit_DATA = \
|
||||
osmo-isdntap.service
|
||||
endif
|
|
@ -0,0 +1,12 @@
|
|||
[Unit]
|
||||
Description=Osmocom ISDN Tap/Trace Daemon
|
||||
Wants=osmo-isdntap.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-isdntap -c /etc/osmocom/osmo-isdntap.cfg
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,6 @@
|
|||
osmo-isdntap (0.0.1) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* initial debian package
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Tue, 30 Jun 2020 18:24:51 +0100
|
|
@ -0,0 +1 @@
|
|||
9
|
|
@ -0,0 +1,24 @@
|
|||
Source: osmo-isdntap
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Harald Welte <laforge@osmocom.org>
|
||||
Build-Depends: debhelper (>=9),
|
||||
dh-autoreconf,
|
||||
autotools-dev,
|
||||
autoconf,
|
||||
automake,
|
||||
libtool,
|
||||
pkg-config,
|
||||
python3-minimal,
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: https://gitea.osmocom.org/retronetworking/osmo-isdntap
|
||||
Vcs-Browser: https://gitea.osmocom.org/retronetworking/osmo-isdntap
|
||||
Homepage: https://projects.osmocom.org/projects/osmo-isdntap
|
||||
|
||||
Package: osmo-isdntap
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: osmo-isdntap: Osmocom's ISDN Tap/Trace daemon
|
|
@ -0,0 +1,20 @@
|
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: osmo-isdntap
|
||||
Source: https://gitea.osmocom.org/retronetworking/osmo-isdntap
|
||||
|
||||
Files: *
|
||||
Copyright: 2022 Harald Welte
|
||||
License: 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 Affero 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/>.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
/etc/osmocom/osmo-isdntap.cfg
|
||||
lib/systemd/system/osmo-isdntap.service
|
||||
usr/bin/osmo-isdntap
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/make -f
|
||||
# You must remove unused comment lines for the released package.
|
||||
# See debhelper(7) (uncomment to enable)
|
||||
# This is an autogenerated template for debian/rules.
|
||||
#
|
||||
# Output every command that modifies files on the build system.
|
||||
#export DH_VERBOSE = 1
|
||||
#
|
||||
# Copy some variable definitions from pkg-info.mk and vendor.mk
|
||||
# under /usr/share/dpkg/ to here if they are useful.
|
||||
#
|
||||
# See FEATURE AREAS/ENVIRONMENT in dpkg-buildflags(1)
|
||||
# Apply all hardening options
|
||||
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
# Package maintainers to append CFLAGS
|
||||
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||
# Package maintainers to append LDFLAGS
|
||||
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||
#
|
||||
# With debhelper version 9 or newer, the dh command exports
|
||||
# all buildflags. So there is no need to include the
|
||||
# /usr/share/dpkg/buildflags.mk file here if compat is 9 or newer.
|
||||
#
|
||||
# These are rarely used code. (START)
|
||||
#
|
||||
# The following include for *.mk magically sets miscellaneous
|
||||
# variables while honoring existing values of pertinent
|
||||
# environment variables:
|
||||
#
|
||||
# Architecture-related variables such as DEB_TARGET_MULTIARCH:
|
||||
#include /usr/share/dpkg/architecture.mk
|
||||
# Vendor-related variables such as DEB_VENDOR:
|
||||
#include /usr/share/dpkg/vendor.mk
|
||||
# Package-related variables such as DEB_DISTRIBUTION
|
||||
#include /usr/share/dpkg/pkg-info.mk
|
||||
#
|
||||
# You may alternatively set them susing a simple script such as:
|
||||
# DEB_VENDOR ?= $(shell dpkg-vendor --query Vendor)
|
||||
#
|
||||
# These are rarely used code. (END)
|
||||
#
|
||||
|
||||
# main packaging script based on dh7 syntax
|
||||
%:
|
||||
dh $@ --with autoreconf
|
||||
|
||||
# debmake generated override targets
|
||||
CONFIGURE_FLAGS += --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- $(CONFIGURE_FLAGS)
|
||||
#
|
||||
# Do not install libtool archive, python .pyc .pyo
|
||||
#override_dh_install:
|
||||
# dh_install --list-missing -X.la -X.pyc -X.pyo
|
||||
|
||||
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
|
||||
override_dh_compress:
|
||||
dh_compress -X.pdf
|
|
@ -0,0 +1,3 @@
|
|||
SUBDIRS = \
|
||||
examples \
|
||||
$(NULL)
|
|
@ -0,0 +1,31 @@
|
|||
OSMOCONF_FILES = \
|
||||
osmo-isdntap.cfg \
|
||||
$(NULL)
|
||||
|
||||
osmoconfdir = $(sysconfdir)/osmocom
|
||||
osmoconf_DATA = $(OSMOCONF_FILES)
|
||||
|
||||
EXTRA_DIST = $(OSMOCONF_FILES)
|
||||
|
||||
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
|
||||
|
||||
dist-hook:
|
||||
for f in $$($(CFG_FILES)); do \
|
||||
j="$(distdir)/$$f" && \
|
||||
mkdir -p "$$(dirname $$j)" && \
|
||||
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
|
||||
done
|
||||
|
||||
install-data-hook:
|
||||
for f in $$($(CFG_FILES)); do \
|
||||
j="$(DESTDIR)$(docdir)/examples/$$f" && \
|
||||
mkdir -p "$$(dirname $$j)" && \
|
||||
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
|
||||
done
|
||||
|
||||
uninstall-hook:
|
||||
@$(PRE_UNINSTALL)
|
||||
for f in $$($(CFG_FILES)); do \
|
||||
j="$(DESTDIR)$(docdir)/examples/$$f" && \
|
||||
$(RM) $$j; \
|
||||
done
|
|
@ -0,0 +1,151 @@
|
|||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-01-28.01
|
||||
|
||||
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# 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 3 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/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# It is probably wise to add these two files to .gitignore, so that you
|
||||
# don't accidentally commit either generated file.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .tarball-version will
|
||||
# exist in distribution tarballs.
|
||||
#
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
case $# in
|
||||
1) ;;
|
||||
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
|
||||
esac
|
||||
|
||||
tarball_version_file=$1
|
||||
nl='
|
||||
'
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || exit 1
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test -z "$v" \
|
||||
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif
|
||||
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
v[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
|
||||
else
|
||||
v=UNKNOWN
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed 's/^v//'`
|
||||
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git status > /dev/null 2>&1
|
||||
|
||||
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d '\012'
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
|
@ -0,0 +1,26 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS=-Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-result \
|
||||
$(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOGSM_CFLAGS)
|
||||
|
||||
|
||||
noinst_HEADERS = \
|
||||
q931.h \
|
||||
log.h \
|
||||
$(NULL)
|
||||
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-isdntap \
|
||||
$(NULL)
|
||||
|
||||
osmo_isdntap_SOURCES = \
|
||||
q931_decode.c \
|
||||
log.c \
|
||||
isdntap.c \
|
||||
osmo-isdntap.c \
|
||||
dahdi.c \
|
||||
vty.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_isdntap_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS)
|
|
@ -0,0 +1,267 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <dahdi/user.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "isdntap.h"
|
||||
|
||||
#define BLOCK_SIZE 512
|
||||
|
||||
/* obtain a per-span sysfs integer attribute */
|
||||
static int get_span_sysfs_int(unsigned int spanno, const char *attr)
|
||||
{
|
||||
int res, rc;
|
||||
char filename[PATH_MAX];
|
||||
FILE *fp;
|
||||
|
||||
snprintf(filename, sizeof(filename), "/sys/bus/dahdi_spans/devices/span-%u/%s", spanno, attr);
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
rc = fscanf(fp, "%d", &res);
|
||||
fclose(fp);
|
||||
|
||||
if (rc == EOF)
|
||||
return -1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* obtain a per-span sysfs string attribute. Caller must free() return value */
|
||||
static char *get_span_sysfs_str(unsigned int spanno, const char *attr)
|
||||
{
|
||||
char *res = NULL;
|
||||
int rc;
|
||||
char filename[PATH_MAX];
|
||||
FILE *fp;
|
||||
|
||||
snprintf(filename, sizeof(filename), "/sys/bus/dahdi_spans/devices/span-%u/%s", spanno, attr);
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
rc = fscanf(fp, "%ms", &res);
|
||||
fclose(fp);
|
||||
|
||||
if (rc == EOF) {
|
||||
free(res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Find the DAHDI span number for the given 'name' */
|
||||
static int get_span_no_by_name(const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* iterate over all (up to 256 max) spans and find a matching name */
|
||||
for (i = 1; i < 256; i++) {
|
||||
char *name_attr = get_span_sysfs_str(i, "name");
|
||||
if (!name_attr)
|
||||
continue;
|
||||
if (!strcmp(name_attr, name)) {
|
||||
free(name_attr);
|
||||
return i;
|
||||
}
|
||||
free(name_attr);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* crate a [rx or tx] mirror FD for given DAHDI channel number */
|
||||
static int make_mirror(long type, int chan)
|
||||
{
|
||||
int res = 0;
|
||||
int fd = 0;
|
||||
struct dahdi_bufferinfo bi;
|
||||
fd = open("/dev/dahdi/pseudo", O_RDONLY);
|
||||
|
||||
memset(&bi, 0, sizeof(bi));
|
||||
bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
|
||||
bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
|
||||
bi.numbufs = 32;
|
||||
bi.bufsize = BLOCK_SIZE;
|
||||
|
||||
ioctl(fd, DAHDI_SET_BUFINFO, &bi);
|
||||
|
||||
res = ioctl(fd, type, &chan);
|
||||
if (res) {
|
||||
fprintf(stderr, "error setting channel err=%d!\n", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
static int dahdi_bchan_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct isdntap_ts *ts = ofd->data;
|
||||
uint8_t buf[4096];
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(what & OSMO_FD_READ);
|
||||
|
||||
rc = read(ofd->fd, buf, sizeof(buf));
|
||||
if (rc <= 0)
|
||||
return -EIO;
|
||||
|
||||
if (ofd->priv_nr == 1) {
|
||||
/* Rx Mirror */
|
||||
} else {
|
||||
/* Tx Mirror */
|
||||
}
|
||||
return isdntap_ts_rx_bchan(ts, buf, rc, 0);
|
||||
}
|
||||
|
||||
static int dahdi_dchan_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct isdntap_ts *ts = ofd->data;
|
||||
uint8_t buf[4096];
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(what & OSMO_FD_READ);
|
||||
|
||||
rc = read(ofd->fd, buf, sizeof(buf));
|
||||
if (rc <= 0)
|
||||
return -EIO;
|
||||
|
||||
if (ofd->priv_nr == 1) {
|
||||
/* Rx Mirror */
|
||||
} else {
|
||||
/* Tx Mirror */
|
||||
}
|
||||
return isdntap_ts_rx_dchan(ts, buf, rc, 0);
|
||||
}
|
||||
|
||||
static int open_line_dahdi(struct isdntap_line *line)
|
||||
{
|
||||
struct isdntap_ts *sig_ts;
|
||||
char *spantype;
|
||||
int rc;
|
||||
|
||||
/* make sure we have a span number */
|
||||
if (line->driver.dahdi.spanno < 0) {
|
||||
if (!line->driver.dahdi.name)
|
||||
return -1;
|
||||
line->driver.dahdi.spanno = get_span_no_by_name(line->driver.dahdi.name);
|
||||
}
|
||||
if (line->driver.dahdi.spanno < 0)
|
||||
return -1;
|
||||
|
||||
/* resolve the base channel number */
|
||||
rc = get_span_sysfs_int(line->driver.dahdi.spanno, "basechan");
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
line->driver.dahdi.basechan = rc;
|
||||
|
||||
/* resolve the numberof channels */
|
||||
rc = get_span_sysfs_int(line->driver.dahdi.spanno, "channels");
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
line->driver.dahdi.channels = rc;
|
||||
|
||||
/* obtain the span type */
|
||||
spantype = get_span_sysfs_str(line->driver.dahdi.spanno, "spantype");
|
||||
if (!spantype)
|
||||
return -1;
|
||||
line->driver.dahdi.spantype = talloc_strdup(line, spantype);
|
||||
free(spantype);
|
||||
|
||||
/* note the DAHDI channel number for each timeslot */
|
||||
for (int i = 0; i < line->driver.dahdi.channels; i++) {
|
||||
/* FIXME: the below is very E1-specific */
|
||||
struct isdntap_ts *ts = &line->ts[1+i];
|
||||
ts->driver.dahdi.channo = line->driver.dahdi.basechan + i;
|
||||
}
|
||||
sig_ts = &line->ts[16]; // FIXME: configurable
|
||||
|
||||
rc = make_mirror(DAHDI_RXMIRROR, sig_ts->driver.dahdi.channo);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
/* verify HDLC mode? */
|
||||
osmo_fd_setup(&sig_ts->driver.dahdi.rx, rc, OSMO_FD_READ, dahdi_dchan_fd_cb, sig_ts, 1);
|
||||
osmo_fd_register(&sig_ts->driver.dahdi.rx);
|
||||
|
||||
rc = make_mirror(DAHDI_TXMIRROR, sig_ts->driver.dahdi.channo);
|
||||
if (rc < 0) {
|
||||
close(sig_ts->driver.dahdi.rx.fd);
|
||||
sig_ts->driver.dahdi.rx.fd = -1;
|
||||
return rc;
|
||||
}
|
||||
/* verify HDLC mode? */
|
||||
osmo_fd_setup(&sig_ts->driver.dahdi.tx, rc, OSMO_FD_READ, dahdi_dchan_fd_cb, sig_ts, 2);
|
||||
osmo_fd_register(&sig_ts->driver.dahdi.tx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open_ts_dahdi(struct isdntap_ts *ts)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = make_mirror(DAHDI_RXMIRROR, ts->driver.dahdi.channo);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
osmo_fd_setup(&ts->driver.dahdi.rx, rc, OSMO_FD_READ, dahdi_bchan_fd_cb, ts, 1);
|
||||
osmo_fd_register(&ts->driver.dahdi.rx);
|
||||
|
||||
rc = make_mirror(DAHDI_TXMIRROR, ts->driver.dahdi.channo);
|
||||
if (rc < 0) {
|
||||
osmo_fd_unregister(&ts->driver.dahdi.rx);
|
||||
close(ts->driver.dahdi.rx.fd);
|
||||
ts->driver.dahdi.rx.fd = -1;
|
||||
return rc;
|
||||
}
|
||||
osmo_fd_setup(&ts->driver.dahdi.tx, rc, OSMO_FD_READ, dahdi_bchan_fd_cb, ts, 2);
|
||||
osmo_fd_register(&ts->driver.dahdi.tx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void close_ts_dahdi(struct isdntap_ts *ts)
|
||||
{
|
||||
if (ts->driver.dahdi.rx.fd >= 0) {
|
||||
osmo_fd_unregister(&ts->driver.dahdi.rx);
|
||||
close(ts->driver.dahdi.rx.fd);
|
||||
ts->driver.dahdi.rx.fd = -1;
|
||||
}
|
||||
if (ts->driver.dahdi.tx.fd >= 0) {
|
||||
osmo_fd_unregister(&ts->driver.dahdi.tx);
|
||||
close(ts->driver.dahdi.tx.fd);
|
||||
ts->driver.dahdi.tx.fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void close_line_dahdi(struct isdntap_line *line)
|
||||
{
|
||||
/* close all file descriptors on all timeslots */
|
||||
for (int i = 0; i < line->driver.dahdi.channels; i++) {
|
||||
/* FIXME: the below is very E1-specific */
|
||||
struct isdntap_ts *ts = &line->ts[1+i];
|
||||
close_ts_dahdi(ts);
|
||||
}
|
||||
}
|
||||
|
||||
const struct isdntap_driver dahdi_driver = {
|
||||
.name = "dahdi",
|
||||
.line_open = open_line_dahdi,
|
||||
.line_close = close_line_dahdi,
|
||||
.ts_open = open_ts_dahdi,
|
||||
.ts_close = close_ts_dahdi,
|
||||
};
|
|
@ -0,0 +1,518 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <osmocom/core/isdnhdlc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/hashtable.h>
|
||||
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "isdntap.h"
|
||||
#include "q931.h"
|
||||
#include "log.h"
|
||||
|
||||
/***********************************************************************
|
||||
* data structures
|
||||
***********************************************************************/
|
||||
|
||||
struct isdntap_line *isdntap_line_find(struct isdntap_instance *itd, const char *name)
|
||||
{
|
||||
struct isdntap_line *line;
|
||||
|
||||
llist_for_each_entry(line, &itd->lines, list) {
|
||||
if (!strcmp(name, line->name))
|
||||
return line;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct isdntap_line *isdntap_line_new(struct isdntap_instance *itd, const char *name)
|
||||
{
|
||||
struct isdntap_line *line = talloc_zero(itd, struct isdntap_line);
|
||||
|
||||
if (!line)
|
||||
return NULL;
|
||||
|
||||
line->name = talloc_strdup(line, name);
|
||||
|
||||
hash_init(line->q931.call_state);
|
||||
//line->ctrs = rate_ctr_group_alloc(line, &line_ctrg_desc, ctr_idx);
|
||||
|
||||
/* initialize timeslots */
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(line->ts); i++) {
|
||||
struct isdntap_ts *ts = &line->ts[i];
|
||||
ts->line = line;
|
||||
ts->num = i;
|
||||
ts->mode = E1_TS_TRACE_MODE_NONE;
|
||||
ts->gsmtap_subtype = GSMTAP_E1T1_LAPD;
|
||||
osmo_isdnhdlc_rcv_init(&ts->hdlc.state[0].vars, 0);
|
||||
osmo_isdnhdlc_rcv_init(&ts->hdlc.state[1].vars, 0);
|
||||
ts->driver.dahdi.rx.fd = -1;
|
||||
ts->driver.dahdi.tx.fd = -1;
|
||||
ts->driver.dahdi.channo = -1;
|
||||
}
|
||||
|
||||
llist_add_tail(&line->list, &itd->lines);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* call state hash table
|
||||
***********************************************************************/
|
||||
|
||||
/* one call / entry in the state table */
|
||||
struct q931_call_state {
|
||||
struct hlist_node node;
|
||||
|
||||
uint32_t call_ref; /* decoded call reference */
|
||||
bool net2user; /* call established in Net->User direction? */
|
||||
time_t create_time; /* time at which call was created */
|
||||
struct q931_channel_id chan_id; /* channel identification of associated user plane */
|
||||
struct q931_party_number calling_party;
|
||||
struct q931_party_number called_party;
|
||||
bool sending_complete; /* called party number is complete */
|
||||
};
|
||||
|
||||
static struct q931_call_state *
|
||||
calltbl_find_callref(struct isdntap_line *line, uint32_t callref)
|
||||
{
|
||||
struct q931_call_state *st;
|
||||
|
||||
hash_for_each_possible(line->q931.call_state, st, node, callref) {
|
||||
if (st->call_ref == callref)
|
||||
return st;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct q931_call_state *
|
||||
calltbl_create_callref(struct isdntap_line *line, uint32_t callref, bool net2user)
|
||||
{
|
||||
struct q931_call_state *st = talloc_zero(line, struct q931_call_state);
|
||||
if (!st)
|
||||
return NULL;
|
||||
|
||||
st->call_ref = callref;
|
||||
st->net2user = net2user;
|
||||
st->create_time = time(NULL);
|
||||
hash_add(line->q931.call_state, &st->node, callref);
|
||||
return st;
|
||||
}
|
||||
|
||||
static struct q931_call_state *
|
||||
calltbl_find_or_create_callref(struct isdntap_line *line, uint32_t callref, bool net2user)
|
||||
{
|
||||
struct q931_call_state *cstate;
|
||||
cstate = calltbl_find_callref(line, callref);
|
||||
if (!cstate)
|
||||
cstate = calltbl_create_callref(line, callref, false);
|
||||
return cstate;
|
||||
}
|
||||
|
||||
static void calltbl_delete_callref(struct q931_call_state *cstate)
|
||||
{
|
||||
hash_del(&cstate->node);
|
||||
talloc_free(cstate);
|
||||
}
|
||||
|
||||
static void calltbl_delete_all_callref(struct isdntap_line *line)
|
||||
{
|
||||
struct q931_call_state *cstate;
|
||||
struct hlist_node *n;
|
||||
int i;
|
||||
|
||||
hash_for_each_safe(line->q931.call_state, i, n, cstate, node) {
|
||||
calltbl_delete_callref(cstate);
|
||||
}
|
||||
}
|
||||
|
||||
/* delete any call_state we have on chan_id */
|
||||
static void calltbl_delete_callref_on_chan_id(struct isdntap_line *line, const struct q931_channel_id *chan_id)
|
||||
{
|
||||
struct q931_call_state *cstate;
|
||||
struct hlist_node *n;
|
||||
int i;
|
||||
|
||||
hash_for_each_safe(line->q931.call_state, i, n, cstate, node) {
|
||||
if (cstate->chan_id.info_chan_type == chan_id->info_chan_type &&
|
||||
cstate->chan_id.d_channel == chan_id->d_channel &&
|
||||
cstate->chan_id.b_channel == chan_id->b_channel) {
|
||||
calltbl_delete_callref(cstate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Q.931 processing
|
||||
***********************************************************************/
|
||||
|
||||
/* append additional digits received (overlap dialling) to called party number */
|
||||
static void append_called_digits(struct q931_party_number *out, const uint8_t *buf, size_t len)
|
||||
{
|
||||
unsigned int idx = strlen(out->digits);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (idx >= ARRAY_SIZE(out->digits))
|
||||
return;
|
||||
out->digits[idx] = buf[i];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
static int isdntap_q931_rx_global(struct isdntap_line *line, const struct q931_msg_parsed *parsed)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (parsed->msg_type) {
|
||||
case Q931_MSGT_RESTART:
|
||||
if (TLVP_PRES_LEN(&parsed->ies, Q931_IEI_RESTART_IND, 1)) {
|
||||
struct q931_channel_id chan_id;
|
||||
uint8_t rest_ind = *TLVP_VAL(&parsed->ies, Q931_IEI_RESTART_IND) & 0x7;
|
||||
switch (rest_ind) {
|
||||
case 0: /* clear indicated channel[s] */
|
||||
if (!TLVP_PRESENT(&parsed->ies, Q931_IEI_CHANNEL_ID))
|
||||
return -EINVAL;
|
||||
rc = q931_decode_channel_id(&chan_id,
|
||||
TLVP_VAL(&parsed->ies, Q931_IEI_CHANNEL_ID),
|
||||
TLVP_LEN(&parsed->ies, Q931_IEI_CHANNEL_ID));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
/* clear any calls on selected channel */
|
||||
calltbl_delete_callref_on_chan_id(line, &chan_id);
|
||||
break;
|
||||
case 6:
|
||||
case 7: /* clear all channels */
|
||||
calltbl_delete_all_callref(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* receive one Q.931 message for signaling analysis */
|
||||
static int isdntap_q931_rx(struct isdntap_line *line, bool net2user, const uint8_t *buf, size_t len)
|
||||
{
|
||||
struct q931_msg_parsed parsed;
|
||||
struct q931_call_state *cstate;
|
||||
int rc;
|
||||
|
||||
rc = q931_msg_parse(&parsed, buf, len);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* process special case of global call reference elsewhere */
|
||||
if (parsed.call_ref == 0) {
|
||||
return isdntap_q931_rx_global(line, &parsed);
|
||||
}
|
||||
|
||||
/* Step 1: look-up (or create in case of SETUP) the call state record for call reference */
|
||||
switch (parsed.msg_type) {
|
||||
case Q931_MSGT_SETUP:
|
||||
cstate = calltbl_find_or_create_callref(line, parsed.call_ref, false);
|
||||
if (!cstate)
|
||||
return -ENOMEM;
|
||||
break;
|
||||
default:
|
||||
cstate = calltbl_find_callref(line, parsed.call_ref);
|
||||
if (!cstate)
|
||||
return -ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 2: Additional information gathering */
|
||||
switch (parsed.msg_type) {
|
||||
case Q931_MSGT_SETUP:
|
||||
/* decode called/calling party (if any) for context */
|
||||
if (TLVP_PRESENT(&parsed.ies, Q931_IEI_CALLED_PARTY_NUM)) {
|
||||
q931_decode_called_party(&cstate->called_party,
|
||||
TLVP_VAL(&parsed.ies, Q931_IEI_CALLED_PARTY_NUM),
|
||||
TLVP_LEN(&parsed.ies, Q931_IEI_CALLED_PARTY_NUM));
|
||||
}
|
||||
if (TLVP_PRESENT(&parsed.ies, Q931_IEI_CALLING_PARTY_NUM)) {
|
||||
q931_decode_calling_party(&cstate->calling_party,
|
||||
TLVP_VAL(&parsed.ies, Q931_IEI_CALLING_PARTY_NUM),
|
||||
TLVP_LEN(&parsed.ies, Q931_IEI_CALLING_PARTY_NUM));
|
||||
}
|
||||
if (TLVP_PRESENT(&parsed.ies, Q931_IEI_SENDING_COMPLETE))
|
||||
cstate->sending_complete = true;
|
||||
break;
|
||||
case Q931_MSGT_INFORMATION:
|
||||
/* append any additional digits to called_party */
|
||||
if (TLVP_PRESENT(&parsed.ies, Q931_IEI_KEYPAD_FACILITY) &&
|
||||
!cstate->sending_complete) {
|
||||
append_called_digits(&cstate->called_party,
|
||||
TLVP_VAL(&parsed.ies, Q931_IEI_KEYPAD_FACILITY),
|
||||
TLVP_LEN(&parsed.ies, Q931_IEI_KEYPAD_FACILITY));
|
||||
}
|
||||
/* mark as complete, if we finally are */
|
||||
if (TLVP_PRESENT(&parsed.ies, Q931_IEI_SENDING_COMPLETE))
|
||||
cstate->sending_complete = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 3: dispatch by message type; look in those that contain a ChannelIndicator */
|
||||
switch (parsed.msg_type) {
|
||||
case Q931_MSGT_SETUP:
|
||||
case Q931_MSGT_CALL_PROCEEDING:
|
||||
case Q931_MSGT_SETUP_ACK:
|
||||
#if 0
|
||||
case Q931_MSGT_ALERTING:
|
||||
case Q931_MSGT_CONNECT:
|
||||
case Q931_MSGT_RESUME_ACK:
|
||||
#endif
|
||||
if (TLVP_PRESENT(&parsed.ies, Q931_IEI_CHANNEL_ID)) {
|
||||
rc = q931_decode_channel_id(&cstate->chan_id,
|
||||
TLVP_VAL(&parsed.ies, Q931_IEI_CHANNEL_ID),
|
||||
TLVP_LEN(&parsed.ies, Q931_IEI_CHANNEL_ID));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (cstate->chan_id.exclusive && !cstate->chan_id.d_channel) {
|
||||
/* now we know the exact B channel used */
|
||||
/* TODO: start capturing */
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Q931_MSGT_RELEASE:
|
||||
case Q931_MSGT_RELEASE_COMPLETE:
|
||||
/* forget about the call */
|
||||
calltbl_delete_callref(cstate);
|
||||
cstate = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* trace one Q.921 / LAPD frame for signaling analysis */
|
||||
static void isdntap_q921_rx(struct isdntap_line *line, bool net2user, const uint8_t *buf, size_t len)
|
||||
{
|
||||
uint8_t sapi, tei;
|
||||
/* Parse LAPD header; Ignore anything != I frames */
|
||||
/* Q.921 header: 2 bytes address; 1-2 byte control; [optional] information */
|
||||
|
||||
if (len < 2)
|
||||
return;
|
||||
|
||||
/* Address field: Figure 5 / Q.921 */
|
||||
sapi = buf[0] >> 2;
|
||||
tei = buf[1] >> 1;
|
||||
|
||||
/* SAPI value for circuit-switched call control (Table 2/Q.921) */
|
||||
if (sapi != 0) {
|
||||
/* skip unknown SAPI */
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < 3)
|
||||
return;
|
||||
|
||||
/* Control field: Table 4/Q.921 */
|
||||
if (buf[2] & 0x01) {
|
||||
/* skip frames != I-frame (which have 0 as lsb of 1st octet) */
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < 4)
|
||||
return;
|
||||
|
||||
/* skip N(R) / N(S) and go directly to Q.931 payload */
|
||||
isdntap_q931_rx(line, net2user, buf + 4, len - 4);
|
||||
}
|
||||
|
||||
/* main entry point for received D-channel data on a given TS */
|
||||
int isdntap_ts_rx_dchan(struct isdntap_ts *ts, const uint8_t *buf, size_t len, bool net2user)
|
||||
{
|
||||
isdntap_q921_rx(ts->line, net2user, buf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* main entry point for received B-channel data on a given TS */
|
||||
int isdntap_ts_rx_bchan(struct isdntap_ts *ts, const uint8_t *buf, size_t len, bool is_rx)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_c(ts->line, len, "Bchan");
|
||||
uint8_t *cur;
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
cur = msgb_put(msg, len);
|
||||
memcpy(cur, buf, len);
|
||||
|
||||
if (is_rx)
|
||||
rc = osmo_wqueue_enqueue(&ts->output.file.rx, msg);
|
||||
else
|
||||
rc = osmo_wqueue_enqueue(&ts->output.file.tx, msg);
|
||||
if (rc)
|
||||
msgb_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* helper function for opening an output file */
|
||||
static int isdntap_open_file(struct isdntap_ts *ts, const char *call_label, const char *suffix)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
int rc;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/isdntap-%s-%u-%s.raw", ts->line->inst->cfg.output_path,
|
||||
call_label, ts->num, suffix);
|
||||
|
||||
rc = open(buf, O_CREAT|O_WRONLY|O_TRUNC|O_NONBLOCK);
|
||||
if (rc < 0) {
|
||||
LOGP(DITAP, LOGL_ERROR, "Error opening file %s: %s\n", buf, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOGP(DITAP, LOGL_INFO, "Opened file %s\n", buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int isdntap_ts_start_bchan(struct isdntap_ts *ts, const char *call_label)
|
||||
{
|
||||
int fd_rx, fd_tx;
|
||||
|
||||
osmo_wqueue_init(&ts->output.file.tx, 100);
|
||||
fd_tx = isdntap_open_file(ts, call_label, "tx");
|
||||
if (fd_tx < 0)
|
||||
return fd_tx;
|
||||
ts->output.file.tx.bfd.fd = fd_tx;
|
||||
osmo_fd_register(&ts->output.file.tx.bfd);
|
||||
|
||||
osmo_wqueue_init(&ts->output.file.rx, 100);
|
||||
fd_rx = isdntap_open_file(ts, call_label, "rx");
|
||||
if (!fd_rx)
|
||||
goto out_cleanup_tx;
|
||||
ts->output.file.rx.bfd.fd = fd_rx;
|
||||
osmo_fd_register(&ts->output.file.rx.bfd);
|
||||
|
||||
return 0;
|
||||
|
||||
out_cleanup_tx:
|
||||
osmo_fd_unregister(&ts->output.file.tx.bfd);
|
||||
ts->output.file.tx.bfd.fd = -1;
|
||||
close(fd_tx);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void isdntap_ts_stop_bchan(struct isdntap_ts *ts)
|
||||
{
|
||||
/* FIXME: ideally we'd make sure that the write queues are both fully flushed first */
|
||||
|
||||
if (ts->output.file.tx.bfd.fd >= 0) {
|
||||
osmo_wqueue_clear(&ts->output.file.tx);
|
||||
osmo_fd_unregister(&ts->output.file.tx.bfd);
|
||||
close(ts->output.file.tx.bfd.fd);
|
||||
ts->output.file.tx.bfd.fd = -1;
|
||||
}
|
||||
|
||||
if (ts->output.file.rx.bfd.fd >= 0) {
|
||||
osmo_wqueue_clear(&ts->output.file.rx);
|
||||
osmo_fd_unregister(&ts->output.file.rx.bfd);
|
||||
close(ts->output.file.rx.bfd.fd);
|
||||
ts->output.file.rx.bfd.fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* e1d integration
|
||||
***********************************************************************/
|
||||
|
||||
# if 0
|
||||
|
||||
/* trace one timeslot of a line */
|
||||
void e1tap_trace_ts(struct e1tap_ts *ts, const uint8_t *tsbuf, size_t frame_count, uint8_t hdlc_idx)
|
||||
{
|
||||
struct e1tap_line *line = ts->line;
|
||||
int oi = 0;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(hdlc_idx <= 0);
|
||||
|
||||
switch (ts->mode) {
|
||||
case E1_TS_TRACE_MODE_HDLC:
|
||||
case E1_TS_TRACE_MODE_ISDN_D:
|
||||
while (oi < frame_count) {
|
||||
int num_consumed;
|
||||
|
||||
/* feed the new bytes into the HDLC decoder */
|
||||
rc = osmo_isdnhdlc_decode(&ts->hdlc.state[hdlc_idx].vars,
|
||||
&tsbuf[oi], frame_count-oi,
|
||||
&num_consumed, ts->hdlc.state[hdlc_idx].out,
|
||||
sizeof(ts->hdlc.state[hdlc_idx].out));
|
||||
if (rc > 0) {
|
||||
/* if HDLC decoder produced output, send it via GSMTAP */
|
||||
gsmtap_send_ex(line->gti, GSMTAP_TYPE_E1T1, flags,
|
||||
ts->num, ts->gsmtap_subtype, 0, 0, 0, 0,
|
||||
ts->hdlc.state[hdlc_idx].out, rc);
|
||||
/* feed D-channel into higher level analysis */
|
||||
if (ts->mode == E1_TS_TRACE_MODE_ISDN_D)
|
||||
isdntap_q921_rx(line, hdlc_idx, ts->hdlc.state[hdlc_idx].out, rc);
|
||||
} else if (rc < 0) {
|
||||
/* FIXME: log error */
|
||||
}
|
||||
oi += num_consumed;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* trace an entire line (we pass in full 32byte frames */
|
||||
void e1tap_trace_line(struct e1tap_line *line, bool mux_out, const uint8_t *buf, int frame_count)
|
||||
{
|
||||
uint8_t tsbuf[frame_count];
|
||||
uint8_t hdlc_idx;
|
||||
|
||||
if (!line)
|
||||
return;
|
||||
|
||||
if (mux_out)
|
||||
hdlc_idx = 1;
|
||||
else
|
||||
hdlc_idx = 0;
|
||||
|
||||
for (unsigned int tn = 1; tn < ARRAY_SIZE(line->ts); tn++) {
|
||||
struct e1tap_ts *ts = line->ts[i];
|
||||
/* fast path */
|
||||
if (ts->mode == E1_TS_TRACE_MODE_NONE)
|
||||
continue;
|
||||
/* demultiplex the bytes of the given TS */
|
||||
for (unsigned int f = 0; f < frame_count; f++)
|
||||
tsbuf[f] = buf[32*f + tn];
|
||||
e1tap_trace_ts(ts, tsbuf, frame_count, hdlc_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* USB/trunkdev <- application/OCTOI */
|
||||
void
|
||||
e1_trace_mux_out(struct e1_line *line, const uint8_t *buf, int frame_count)
|
||||
{
|
||||
return _e1_trace(line, true, buf, frame_count);
|
||||
}
|
||||
|
||||
/* USB/trunkdev -> application/OCTOI */
|
||||
void
|
||||
e1_trace_mux_in(struct e1_line *line, const uint8_t *buf, int frame_count)
|
||||
{
|
||||
return _e1_trace(line, false, buf, frame_count);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,125 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/isdnhdlc.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
enum {
|
||||
ISDNTAP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
LINE_NODE,
|
||||
};
|
||||
|
||||
/* number of octets in an information field */
|
||||
#define Q921_N201 260
|
||||
#define Q921_ADDR_SIZE 2
|
||||
#define Q921_CTRL_SIZE 2
|
||||
#define Q921_FCS_SIZE 2
|
||||
|
||||
#define HDLC_FRAME_SIZE (Q921_N201 + Q921_ADDR_SIZE + Q921_CTRL_SIZE + Q921_FCS_SIZE)
|
||||
|
||||
|
||||
struct isdntap_line;
|
||||
|
||||
enum isdntap_ts_mode {
|
||||
E1_TS_TRACE_MODE_NONE,
|
||||
E1_TS_TRACE_MODE_RAW, /* RAW bitstream */
|
||||
E1_TS_TRACE_MODE_HDLC, /* generic HDLC mode */
|
||||
E1_TS_TRACE_MODE_ISDN_D, /* ISDN D-Channel (Q.921 + Q.931) */
|
||||
};
|
||||
|
||||
/* one "complete" HDLC decoder state */
|
||||
struct isdntap_hdlc_state {
|
||||
struct osmo_isdnhdlc_vars vars;
|
||||
uint8_t out[HDLC_FRAME_SIZE];
|
||||
};
|
||||
|
||||
/* isdntap state for one timeslot */
|
||||
struct isdntap_ts {
|
||||
struct isdntap_line *line; /* back-pointer */
|
||||
uint8_t num; /* timeslot number */
|
||||
enum isdntap_ts_mode mode;
|
||||
uint8_t gsmtap_subtype; // GSMTAP_E1T1_LAPD
|
||||
struct {
|
||||
/* two soft-HDLC instances, one for each direction */
|
||||
struct isdntap_hdlc_state state[2];
|
||||
} hdlc;
|
||||
|
||||
union {
|
||||
struct {
|
||||
struct osmo_fd rx; /* RX-mirror file descriptor */
|
||||
struct osmo_fd tx; /* TX-mirror file descriptor */
|
||||
int channo; /* DAHDI channel number for this TS */
|
||||
} dahdi;
|
||||
} driver;
|
||||
|
||||
union {
|
||||
struct {
|
||||
struct osmo_wqueue rx;
|
||||
struct osmo_wqueue tx;
|
||||
} file;
|
||||
} output;
|
||||
};
|
||||
|
||||
/* isdntap state for one line/span */
|
||||
struct isdntap_line {
|
||||
struct llist_head list; /* member in list of all isdntap_lines */
|
||||
const char *name; /* human-readable name */
|
||||
struct isdntap_instance *inst; /* back-pointer */
|
||||
struct gsmtap_inst *gti; /* gsmtap instance through which we log */
|
||||
void *priv; /* private back-pointer (e.g. to e1d line) */
|
||||
|
||||
struct isdntap_ts ts[32]; /* per-timeslot state */
|
||||
|
||||
/* signalling state */
|
||||
struct {
|
||||
} q921;
|
||||
struct {
|
||||
DECLARE_HASHTABLE(call_state, 8);
|
||||
} q931;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
|
||||
union {
|
||||
struct {
|
||||
const char *name;
|
||||
const char *spantype;
|
||||
int spanno;
|
||||
int basechan;
|
||||
int channels;
|
||||
} dahdi;
|
||||
} driver;
|
||||
};
|
||||
|
||||
struct isdntap_driver {
|
||||
const char *name;
|
||||
int (*line_open)(struct isdntap_line *line);
|
||||
void (*line_close)(struct isdntap_line *line);
|
||||
int (*ts_open)(struct isdntap_ts *ts);
|
||||
void (*ts_close)(struct isdntap_ts *ts);
|
||||
};
|
||||
|
||||
/* drivers feed data into the isdntap core via this API */
|
||||
int isdntap_ts_rx_dchan(struct isdntap_ts *ts, const uint8_t *buf, size_t len, bool net2user);
|
||||
int isdntap_ts_rx_bchan(struct isdntap_ts *ts, const uint8_t *buf, size_t len, bool net2user);
|
||||
|
||||
/* DAHDI driver */
|
||||
extern const struct isdntap_driver dahdi_driver;
|
||||
|
||||
struct isdntap_instance {
|
||||
struct llist_head lines;
|
||||
struct {
|
||||
const char *output_path;
|
||||
} cfg;
|
||||
};
|
||||
|
||||
struct isdntap_line *isdntap_line_find(struct isdntap_instance *itd, const char *name);
|
||||
struct isdntap_line *isdntap_line_new(struct isdntap_instance *itd, const char *name);
|
||||
|
||||
void isdntap_vty_init(struct isdntap_instance *itd);
|
||||
|
||||
int isdntap_ts_start_bchan(struct isdntap_ts *ts, const char *call_label);
|
||||
void isdntap_ts_stop_bchan(struct isdntap_ts *ts);
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* log.c
|
||||
*
|
||||
* (C) 2022 by Harald Welte <laforge@osmocom.org>
|
||||
*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
[DITAP] = {
|
||||
.name = "DITAP",
|
||||
.loglevel = LOGL_INFO,
|
||||
.enabled = 1,
|
||||
},
|
||||
[DQ931] = {
|
||||
.name = "DQ931",
|
||||
.loglevel = LOGL_NOTICE,
|
||||
.enabled = 1,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = default_categories,
|
||||
.num_cat = ARRAY_SIZE(default_categories),
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* log.h
|
||||
*
|
||||
* (C) 2022 by Harald Welte <laforge@osmocom.org>
|
||||
*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
enum {
|
||||
DITAP,
|
||||
DQ931,
|
||||
};
|
||||
|
||||
extern const struct log_info log_info;
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* osmo-isdntap.c
|
||||
*
|
||||
* (C) 2022 by Harald Welte <laforge@osmocom.org>
|
||||
*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
|
||||
#include "isdntap.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifndef OSMO_VTY_PORT_ITAP
|
||||
#define OSMO_VTY_PORT_ITAP 4269
|
||||
#endif
|
||||
|
||||
static const char *g_config_file = "osmo-isdntap.cfg";
|
||||
static void *g_itap_ctx = NULL;
|
||||
static int g_shutdown = 0;
|
||||
|
||||
|
||||
static void sig_handler(int signo)
|
||||
{
|
||||
fprintf(stdout, "signal %d received\n", signo);
|
||||
switch (signo) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
fprintf(stdout, "shutting down\n");
|
||||
g_shutdown = 1;
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGUSR1:
|
||||
talloc_report(g_itap_ctx, stderr);
|
||||
talloc_report_full(g_itap_ctx, stderr);
|
||||
break;
|
||||
case SIGUSR2:
|
||||
talloc_report_full(g_itap_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "osmo-isdntap",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = vty_go_parent,
|
||||
.copyright =
|
||||
"(C) 2022 by Harald Welte and contributors\r\n",
|
||||
"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\r\n"
|
||||
"This is free software: you are free to change and redistribute it.\r\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\r\n",
|
||||
};
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
printf(" Some useful help...\n");
|
||||
printf(" -h --help This text.\n");
|
||||
printf(" -d --debug option --debug=DITAP:DQ931 enable debugging.\n");
|
||||
printf(" -c --config-file filename The config file to use.\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static const struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"debug", 1, 0, 'd'},
|
||||
{"config-file", 1, 0, 'c'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hd:c:", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'd':
|
||||
log_parse_category_mask(osmo_stderr_target, optarg);
|
||||
break;
|
||||
case 'c':
|
||||
g_config_file = optarg;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Error in command line options. Exiting.\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > optind) {
|
||||
fprintf(stderr, "Unsupported positional arguments on command line\n");
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct isdntap_instance *itd = NULL;
|
||||
int rv;
|
||||
|
||||
/* talloc init */
|
||||
g_itap_ctx = talloc_named_const(NULL, 0, "osmo-isdntap");
|
||||
msgb_talloc_ctx_init(g_itap_ctx, 0);
|
||||
vty_info.tall_ctx = g_itap_ctx;
|
||||
|
||||
/* logging init */
|
||||
osmo_init_logging2(g_itap_ctx, &log_info);
|
||||
|
||||
/* signals init */
|
||||
signal(SIGINT, &sig_handler);
|
||||
signal(SIGTERM, &sig_handler);
|
||||
signal(SIGABRT, &sig_handler);
|
||||
signal(SIGUSR1, &sig_handler);
|
||||
signal(SIGUSR2, &sig_handler);
|
||||
osmo_init_ignore_signals();
|
||||
|
||||
/* main state */
|
||||
itd = talloc_zero(g_itap_ctx, struct isdntap_instance);
|
||||
OSMO_ASSERT(itd);
|
||||
INIT_LLIST_HEAD(&itd->lines);
|
||||
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds();
|
||||
isdntap_vty_init(itd);
|
||||
rate_ctr_init(itd);
|
||||
osmo_stats_init(itd);
|
||||
osmo_stat_item_init(itd);
|
||||
osmo_stats_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_fsm_vty_add_cmds();
|
||||
osmo_cpu_sched_vty_init(itd);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
rv = vty_read_config_file(g_config_file, NULL);
|
||||
if (rv < 0) {
|
||||
LOGP(DITAP, LOGL_FATAL, "Failed to parse the config file '%s'\n", g_config_file);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
rv = telnet_init_dynif(g_itap_ctx, itd, vty_get_bind_addr(), OSMO_VTY_PORT_ITAP);
|
||||
if (rv != 0) {
|
||||
LOGP(DITAP, LOGL_FATAL, "Failed to bind VTY interface to %s:%u\n",
|
||||
vty_get_bind_addr(), OSMO_VTY_PORT_ITAP);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* main loop */
|
||||
while (!g_shutdown) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
talloc_free(itd);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
/* Table 4-2/Q.931 - Message types */
|
||||
enum q931_msg_type {
|
||||
/* Call establishment messages */
|
||||
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 messages */
|
||||
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_SUSPEND_REJ = 0x21,
|
||||
Q931_MSGT_USER_INFO = 0x20,
|
||||
/* Call clearing messages */
|
||||
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_CTRL = 0x79,
|
||||
Q931_MSGT_INFORMATION = 0x7b,
|
||||
Q931_MSGT_NOTIFY = 0x6e,
|
||||
Q931_MSGT_STATUS = 0x7d,
|
||||
Q931_MSGT_STATUS_ENQIURY = 0x75,
|
||||
};
|
||||
|
||||
/* Table 4-3/Q.931 */
|
||||
enum q931_iei {
|
||||
/* reserved */
|
||||
/* shift */
|
||||
Q931_IEI_MORE_DATA = 0xa0,
|
||||
Q931_IEI_SENDING_COMPLETE = 0xa1,
|
||||
/* contentionlevel */
|
||||
/* repeat indicator */
|
||||
/* Veriable length IEs */
|
||||
Q931_IEI_SEGMENTED_MSG = 0x00,
|
||||
Q931_IEI_BEARER_CAP = 0x04,
|
||||
Q931_IEI_CAUSE = 0x08,
|
||||
Q931_IEI_CALL_ID = 0x10,
|
||||
Q931_IEI_CALL_STATE = 0x14,
|
||||
Q931_IEI_CHANNEL_ID = 0x18,
|
||||
Q931_IEI_PROGRESS_IND = 0x1e,
|
||||
Q931_IEI_NETWORK_SPEC_FAC = 0x20,
|
||||
Q931_IEI_NOTIFICATION_IND = 0x27,
|
||||
Q931_IEI_DISPLAY = 0x28,
|
||||
Q931_IEI_DATE_TIME = 0x29,
|
||||
Q931_IEI_KEYPAD_FACILITY = 0x2c,
|
||||
Q931_IEI_SIGNAL = 0x34,
|
||||
Q931_IEI_INFORMATION_RATE = 0x40,
|
||||
Q931_IEI_E2E_TRANSIT_DELAY = 0x42,
|
||||
Q931_IEI_TRANSIT_DELAY_SEL_AND_IND = 0x43,
|
||||
Q931_IEI_PKT_LAYER_BIN_PARAMS = 0x44,
|
||||
Q931_IEI_PKT_LAYER_WIN_SIZE = 0x45,
|
||||
Q931_IEI_PACKET_SIZE = 0x46,
|
||||
Q931_IEI_CLOSED_USER_GROUP = 0x47,
|
||||
Q931_IEI_REV_CHARGING_IND = 0x4a,
|
||||
Q931_IEI_CALLING_PARTY_NUM = 0x5c,
|
||||
Q931_IEI_CALLING_PARTY_SUBADDR = 0x5d,
|
||||
Q931_IEI_CALLED_PARTY_NUM = 0x70,
|
||||
Q931_IEI_CALLED_PARTY_SUBADDR = 0x71,
|
||||
Q931_IEI_TRANSIT_NET_SEL = 0x78,
|
||||
Q931_IEI_RESTART_IND = 0x79,
|
||||
Q931_IEI_LOW_LAYER_COMPAT = 0x7c,
|
||||
Q931_IEI_HIGH_LAYER_COMPAT = 0x7d,
|
||||
Q931_IEI_ESCAPE_FOR_EXT = 0x7f,
|
||||
};
|
||||
|
||||
struct q931_msg_parsed {
|
||||
uint8_t msg_type;
|
||||
uint32_t call_ref;
|
||||
struct tlv_parsed ies;
|
||||
};
|
||||
|
||||
int q931_msg_parse(struct q931_msg_parsed *out, const uint8_t *buf, size_t len);
|
||||
|
||||
uint32_t q931_decode_callref(const uint8_t *data, uint8_t len);
|
||||
|
||||
/* Q.931 Section 4.5.13 */
|
||||
enum q931_info_chan_type {
|
||||
Q931_INFO_CHAN_T_NONE,
|
||||
Q931_INFO_CHAN_T_ANY,
|
||||
Q931_INFO_CHAN_T_SPECIFIC,
|
||||
};
|
||||
|
||||
/* decoded version of (simplified) Q.931 Channel Identification IE */
|
||||
struct q931_channel_id {
|
||||
bool interface_id_present;
|
||||
uint32_t interface_id;
|
||||
bool interface_type_pri;
|
||||
bool exclusive;
|
||||
bool d_channel;
|
||||
enum q931_info_chan_type info_chan_type;
|
||||
uint32_t b_channel; /* 1..2 on BRI; 1..31 on PRI */
|
||||
};
|
||||
|
||||
|
||||
int q931_decode_channel_id(struct q931_channel_id *out, const uint8_t *data, uint8_t len);
|
||||
|
||||
struct q931_party_number {
|
||||
uint8_t type_of_number;
|
||||
uint8_t numbering_plan_id;
|
||||
uint8_t presentation_ind;
|
||||
uint8_t screening_ind;
|
||||
char digits[32];
|
||||
};
|
||||
|
||||
int q931_decode_called_party(struct q931_party_number *out, const uint8_t *buf, size_t len);
|
||||
int q931_decode_calling_party(struct q931_party_number *out, const uint8_t *buf, size_t len);
|
|
@ -0,0 +1,261 @@
|
|||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "q931.h"
|
||||
|
||||
/* Table 4-3/Q.931 */
|
||||
const struct tlv_definition q931_tlv_def = {
|
||||
.def = {
|
||||
/* fixed-length */
|
||||
[0x80] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
[0x90] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
[Q931_IEI_MORE_DATA] = { TLV_TYPE_T, 0 },
|
||||
[Q931_IEI_SENDING_COMPLETE] = { TLV_TYPE_T, 0 },
|
||||
[0xB0] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
[0xD0] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
/* variable-length */
|
||||
[Q931_IEI_SEGMENTED_MSG] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_BEARER_CAP] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CAUSE] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CALL_ID] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CALL_STATE] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CHANNEL_ID] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_PROGRESS_IND] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_NETWORK_SPEC_FAC] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_NOTIFICATION_IND] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_DISPLAY] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_DATE_TIME] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_KEYPAD_FACILITY] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_SIGNAL] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_INFORMATION_RATE] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_E2E_TRANSIT_DELAY] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_TRANSIT_DELAY_SEL_AND_IND] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_PKT_LAYER_BIN_PARAMS] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_PKT_LAYER_WIN_SIZE] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_PACKET_SIZE] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CLOSED_USER_GROUP] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_REV_CHARGING_IND] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CALLING_PARTY_NUM] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CALLING_PARTY_SUBADDR]= { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CALLED_PARTY_NUM] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_CALLED_PARTY_SUBADDR] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_TRANSIT_NET_SEL] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_RESTART_IND] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_LOW_LAYER_COMPAT] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_HIGH_LAYER_COMPAT] = { TLV_TYPE_TLV, 0 },
|
||||
[Q931_IEI_ESCAPE_FOR_EXT] = { TLV_TYPE_TLV, 0 },
|
||||
},
|
||||
};
|
||||
|
||||
/* parse one Q.931 message for signaling analysis */
|
||||
int q931_msg_parse(struct q931_msg_parsed *out, const uint8_t *buf, size_t len)
|
||||
{
|
||||
uint8_t cref_len;
|
||||
uint32_t cref;
|
||||
const uint8_t *other_ie;
|
||||
size_t other_ie_len;
|
||||
int rc;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
/* at least protocol discriminator + length of call-ref must be present */
|
||||
if (len < 2)
|
||||
return -EMSGSIZE;
|
||||
|
||||
/* check protocol discriminator */
|
||||
if (buf[0] != 0x08)
|
||||
return -EINVAL;
|
||||
|
||||
/* parse [variable length] call reference */
|
||||
cref_len = buf[1] & 0x0F;
|
||||
if (len < 2 + cref_len)
|
||||
return -EMSGSIZE;
|
||||
out->call_ref = q931_decode_callref(buf+1, len-1);
|
||||
|
||||
/* parse message type */
|
||||
if (len < 2 + cref_len + 1)
|
||||
return -EMSGSIZE;
|
||||
out->msg_type = buf[2+cref_len] & 0x7f;
|
||||
|
||||
/* parse 'other IEs' */
|
||||
other_ie = buf + 2 + cref_len + 1;
|
||||
other_ie_len = len - (2 + cref_len + 1);
|
||||
if (other_ie_len) {
|
||||
rc = tlv_parse(&out->ies, &q931_tlv_def, other_ie, other_ie_len, 0, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Q.931 Section 4.3 */
|
||||
uint32_t q931_decode_callref(const uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t len_of_callref;
|
||||
uint32_t callref = 0;
|
||||
bool flag;
|
||||
|
||||
if (len < 1)
|
||||
return 0;
|
||||
|
||||
len_of_callref = data[0] & 0x0f;
|
||||
if (len_of_callref = 0)
|
||||
return 0;
|
||||
|
||||
if (len_of_callref > 4)
|
||||
return 0;
|
||||
|
||||
if (len - 1 < len_of_callref)
|
||||
return 0;
|
||||
|
||||
/* first octet contains flag, needs special handling */
|
||||
if (data[1] & 0x80)
|
||||
flag = 1;
|
||||
callref = data[1] & 0x7f;
|
||||
|
||||
/* all further octets ... */
|
||||
for (unsigned i = 1; i < len_of_callref; i++)
|
||||
callref = (callref << 8) | data[i];
|
||||
|
||||
if (flag)
|
||||
callref |= 0x80000000;
|
||||
|
||||
return callref;
|
||||
}
|
||||
|
||||
/* Decode a (subset of) Q.931 channel identification IE from binary to struct representation */
|
||||
int q931_decode_channel_id(struct q931_channel_id *out, const uint8_t *data, uint8_t len)
|
||||
{
|
||||
const uint8_t *cur = data;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (len < 1)
|
||||
return -1;
|
||||
|
||||
if (! (data[0] & 0x80))
|
||||
return -1;
|
||||
|
||||
if (data[0] & 0x40) {
|
||||
out->interface_id_present = true;
|
||||
cur++;
|
||||
if (len < 1 + (cur - data))
|
||||
return -1;
|
||||
/* we only support single-octet interface ID */
|
||||
if (!(*cur & 0x80))
|
||||
return -1;
|
||||
out->interface_id = *cur & 0x7f;
|
||||
}
|
||||
|
||||
if (data[0] & 0x20)
|
||||
out->interface_type_pri = true;
|
||||
|
||||
if (data[0] & 0x08)
|
||||
out->exclusive = true;
|
||||
|
||||
if (data[0] & 0x04) {
|
||||
out->d_channel = true;
|
||||
} else {
|
||||
switch (data[0] & 0x03) {
|
||||
case 0:
|
||||
out->info_chan_type = Q931_INFO_CHAN_T_NONE;
|
||||
break;
|
||||
case 1:
|
||||
if (out->interface_type_pri == false) {
|
||||
out->b_channel = 1;
|
||||
} else {
|
||||
/* Octet 3.2 */
|
||||
cur++;
|
||||
if (len < 1 + (cur - data))
|
||||
return -1;
|
||||
if (!(*cur & 0x80))
|
||||
return -1;
|
||||
/* do we care about the coding standard? */
|
||||
/* we only support single channel numbers */
|
||||
if (*cur & 0x10)
|
||||
return -1;
|
||||
/* we only support B-channels */
|
||||
if (*cur & 0x0f != 0x03)
|
||||
return -1;
|
||||
/* Octet 3.3 */
|
||||
cur++;
|
||||
if (len < 1 + (cur - data))
|
||||
return -1;
|
||||
/* we only support single channel numbers */
|
||||
if (!(*cur & 0x80))
|
||||
return -1;
|
||||
out->b_channel = *cur & 0x7f;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (out->interface_type_pri == false)
|
||||
out->b_channel = 2;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 3:
|
||||
out->info_chan_type = Q931_INFO_CHAN_T_ANY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Q.931 Section 4.5.8 */
|
||||
int q931_decode_called_party(struct q931_party_number *out, const uint8_t *buf, size_t len)
|
||||
{
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (len < 1)
|
||||
return -1;
|
||||
|
||||
/* Octet 3 */
|
||||
if (!(*buf & 0x80))
|
||||
return -1;
|
||||
out->type_of_number = (*buf >> 4) & 0x7;
|
||||
out->numbering_plan_id = *buf & 0xf;
|
||||
|
||||
for (unsigned int i = 0; i < len - 1; i++) {
|
||||
if (i >= sizeof(out->digits))
|
||||
return -1;
|
||||
out->digits[i] = buf[1+i] & 0x7f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Q.931 Section 4.5.10 */
|
||||
int q931_decode_calling_party(struct q931_party_number *out, const uint8_t *buf, size_t len)
|
||||
{
|
||||
const uint8_t *cur = buf;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (len < 1)
|
||||
return -1;
|
||||
|
||||
/* Octet 3 */
|
||||
out->type_of_number = (*cur >> 4) & 0x7;
|
||||
out->numbering_plan_id = *cur & 0xf;
|
||||
if (!(*cur & 0x80)) {
|
||||
/* Octet 3a */
|
||||
cur++;
|
||||
if (len < 2)
|
||||
return -1;
|
||||
if (!(*cur & 0x80))
|
||||
return -1;
|
||||
out->presentation_ind = (*cur >> 5) & 0x3;
|
||||
out->screening_ind = *cur & 0x3;
|
||||
}
|
||||
cur++;
|
||||
|
||||
for (unsigned int i = 0; i < len - (cur - buf); i++) {
|
||||
if (i >= sizeof(out->digits))
|
||||
return -1;
|
||||
out->digits[i] = cur[i] & 0x7f;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/* osmo-isdntap VTY interface */
|
||||
/* (C) 2022 by Harald Welte <laforge@osmocom.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* struct ucred */
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/buffer.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/tdef_vty.h>
|
||||
|
||||
#include "isdntap.h"
|
||||
|
||||
static struct isdntap_instance *vty_itd;
|
||||
|
||||
static struct cmd_node isdntap_node = {
|
||||
(enum node_type) ISDNTAP_NODE,
|
||||
"%s(config-isdntap)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node line_node = {
|
||||
(enum node_type) LINE_NODE,
|
||||
"%s(config-isdntap-line)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static void vty_dump_line(struct vty *vty, const struct isdntap_line *line)
|
||||
{
|
||||
vty_out(vty, "Line %s%s", line->name, VTY_NEWLINE);
|
||||
if (1) {
|
||||
vty_out(vty, " Driver: DAHDI, span_name=%s, span_type=%s, span_no=%d, "
|
||||
"basechan=%d, channels=%d%s",
|
||||
line->driver.dahdi.name, line->driver.dahdi.spantype,
|
||||
line->driver.dahdi.spanno, line->driver.dahdi.basechan,
|
||||
line->driver.dahdi.channels, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
//vty_out_rate_ctr_group(vty, " ", line->ctrs);
|
||||
}
|
||||
|
||||
DEFUN(show_line, show_line_cmd, "show line",
|
||||
SHOW_STR "Display information about an E1 Line\n")
|
||||
{
|
||||
struct isdntap_line *line;
|
||||
|
||||
llist_for_each_entry(line, &vty_itd->lines, list) {
|
||||
vty_dump_line(vty, line);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_isdntap, cfg_isdntap_cmd, "isdntap",
|
||||
"isdntap Daemon specific configuration\n")
|
||||
{
|
||||
vty->node = ISDNTAP_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_itd_line_dahdi, cfg_itd_line_dahdi_cmd, "line NAME dahdi",
|
||||
"Configure an ISDNtap E1 line\n"
|
||||
"E1 Line (Span) Name\n"
|
||||
"Use the DAHDI Driver\n")
|
||||
{
|
||||
const char *name = argv[0];
|
||||
struct isdntap_line *line;
|
||||
|
||||
line = isdntap_line_find(vty_itd, name);
|
||||
if (!line) {
|
||||
line = isdntap_line_new(vty_itd, name);
|
||||
}
|
||||
if (!line) {
|
||||
vty_out(vty, "%% Could not create line%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
//line->driver = E1_DRIVER_DAHDI;
|
||||
|
||||
vty->index = line;
|
||||
vty->node = LINE_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#if 0
|
||||
DEFUN(cfg_e1d_if_usb_serial, cfg_e1d_if_usb_serial_cmd,
|
||||
"usb-serial SERNO",
|
||||
"Configure the USB serial number of an E1 interface device\n"
|
||||
"iSerial string\n")
|
||||
{
|
||||
struct e1_intf *intf = vty->index;
|
||||
|
||||
if (intf->drv != E1_DRIVER_USB)
|
||||
return CMD_WARNING;
|
||||
|
||||
osmo_talloc_replace_string(intf, &intf->usb.serial_str, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_e1d_if_line_mode, cfg_e1d_if_line_mode_cmd,
|
||||
"mode (channelized|superchannel|e1oip)",
|
||||
"Configure the mode of the E1 line\n"
|
||||
"Channelized (64kBps timeslot) mode\n"
|
||||
"Superchannel (1xHDLC over 31x64kBps) mode\n")
|
||||
{
|
||||
struct e1_line *line = vty->index;
|
||||
enum e1_line_mode new_mode = get_string_value(e1_line_mode_names, argv[0]);
|
||||
if (line->mode != new_mode) {
|
||||
/* FIXME: clean up any old state */
|
||||
line->mode = new_mode;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int config_write_line(struct vty *vty, struct isdntap_line *line)
|
||||
{
|
||||
vty_out(vty, " line %s dahdi%s", line->name, VTY_NEWLINE);
|
||||
//vty_out(vty, " mode %s%s", get_value_string(e1_line_mode_names, line->mode), VTY_NEWLINE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_write_isdntap(struct vty *vty)
|
||||
{
|
||||
struct isdntap_line *line;
|
||||
|
||||
vty_out(vty, "isdntap%s", VTY_NEWLINE);
|
||||
|
||||
/* find all vpair interfaces */
|
||||
llist_for_each_entry(line, &vty_itd->lines, list) {
|
||||
config_write_line(vty, line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void isdntap_vty_init(struct isdntap_instance *itd)
|
||||
{
|
||||
vty_itd = itd;
|
||||
install_element_ve(&show_line_cmd);
|
||||
|
||||
install_node(&isdntap_node, config_write_isdntap);
|
||||
install_element(CONFIG_NODE, &cfg_isdntap_cmd);
|
||||
//install_element(ISDNTAP_NODE, &cfg_vpair_cmd);
|
||||
|
||||
install_node(&line_node, NULL);
|
||||
install_element(ISDNTAP_NODE, &cfg_itd_line_dahdi_cmd);
|
||||
//install_element(LINE_NODE, &cfg_isdntap_if_line_cmd);
|
||||
//install_element(LINE_NODE, &cfg_isdntap_if_usb_serial_cmd);
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
SUBDIRS = \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
|
||||
:;{ \
|
||||
echo '# Signature of the current package.' && \
|
||||
echo 'm4_define([AT_PACKAGE_NAME],' && \
|
||||
echo ' [$(PACKAGE_NAME)])' && \
|
||||
echo 'm4_define([AT_PACKAGE_TARNAME],' && \
|
||||
echo ' [$(PACKAGE_TARNAME)])' && \
|
||||
echo 'm4_define([AT_PACKAGE_VERSION],' && \
|
||||
echo ' [$(PACKAGE_VERSION)])' && \
|
||||
echo 'm4_define([AT_PACKAGE_STRING],' && \
|
||||
echo ' [$(PACKAGE_STRING)])' && \
|
||||
echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
|
||||
echo ' [$(PACKAGE_BUGREPORT)])'; \
|
||||
echo 'm4_define([AT_PACKAGE_URL],' && \
|
||||
echo ' [$(PACKAGE_URL)])'; \
|
||||
} >'$(srcdir)/package.m4'
|
||||
|
||||
EXTRA_DIST = \
|
||||
testsuite.at \
|
||||
$(srcdir)/package.m4 \
|
||||
$(TESTSUITE) \
|
||||
$(NULL)
|
||||
|
||||
TESTSUITE = $(srcdir)/testsuite
|
||||
|
||||
DISTCLEANFILES = \
|
||||
atconfig \
|
||||
$(NULL)
|
||||
|
||||
if ENABLE_EXT_TESTS
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
$(MAKE) vty-test
|
||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
$(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
|
||||
$(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v
|
||||
else
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
echo "Not running python-based tests (determined at configure-time)"
|
||||
endif
|
||||
|
||||
# Run a specific test with: 'make vty-test VTY_TEST=osmo-isdntap.vty'
|
||||
VTY_TEST ?= *.vty
|
||||
|
||||
# To update the VTY script from current application behavior,
|
||||
# pass -u to vty_script_runner.py by doing:
|
||||
# make vty-test U=-u
|
||||
vty-test:
|
||||
osmo_verify_transcript_vty.py -v \
|
||||
-n osmo-isdntap -p 4239 \
|
||||
-r "$(top_builddir)/src/osmo-isdntap -c $(top_srcdir)/doc/examples/osmo-isdntap.cfg" \
|
||||
$(U) $(srcdir)/$(VTY_TEST)
|
||||
|
||||
check-local: atconfig $(TESTSUITE)
|
||||
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
|
||||
$(MAKE) $(AM_MAKEFLAGS) python-tests
|
||||
|
||||
installcheck-local: atconfig $(TESTSUITE)
|
||||
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
|
||||
$(TESTSUITEFLAGS)
|
||||
|
||||
clean-local:
|
||||
test ! -f '$(TESTSUITE)' || \
|
||||
$(SHELL) '$(TESTSUITE)' --clean
|
||||
|
||||
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
|
||||
AUTOTEST = $(AUTOM4TE) --language=autotest
|
||||
$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
|
||||
$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
|
||||
mv $@.tmp $@
|
Loading…
Reference in New Issue