summaryrefslogtreecommitdiffstats
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/PyCatcher/GUI/catcher_main.glade270
-rw-r--r--Src/PyCatcher/src/evaluators.py54
-rw-r--r--Src/PyCatcher/src/pyCatcherController.py17
-rw-r--r--Src/PyCatcher/src/pyCatcherView.py58
-rw-r--r--Src/PyCatcher/src/settings.py6
-rw-r--r--Src/osmolib/src/host/layer23/src/misc/Makefile.am3
-rwxr-xr-xSrc/osmolib/src/host/layer23/src/misc/enc_scanbin0 -> 896781 bytes
-rw-r--r--Src/osmolib/src/host/layer23/src/misc/enc_scan.c556
-rw-r--r--Src/osmolib/src/host/layer23/src/misc/enc_scan_old.c785
9 files changed, 1726 insertions, 23 deletions
diff --git a/Src/PyCatcher/GUI/catcher_main.glade b/Src/PyCatcher/GUI/catcher_main.glade
index 9a75849..eae5beb 100644
--- a/Src/PyCatcher/GUI/catcher_main.glade
+++ b/Src/PyCatcher/GUI/catcher_main.glade
@@ -305,6 +305,163 @@ CSV
</object>
</child>
</object>
+ <object class="GtkDialog" id="encryption_window">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="type">popup</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="transient_for">main_window</property>
+ <property name="has_resize_grip">False</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox7">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area7">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="btn_encryption_close">
+ <property name="label" translatable="yes">Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="_on_encryption_close_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="box7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkHBox" id="box8h">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">ARFCNs to scan: </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="te_enc_arfcns">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">•</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="box8h2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Timeout: </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="te_enc_timeout">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">•</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="btn_scan_enc">
+ <property name="label" translatable="yes">Scan base stations!</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="_on_scan_enc_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="lbl_enc_result">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Result: Empty</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">btn_encryption_close</action-widget>
+ </action-widgets>
+ </object>
<object class="GtkWindow" id="evaluation_window">
<property name="can_focus">False</property>
<property name="title" translatable="yes">ICDS</property>
@@ -405,7 +562,7 @@ CSV
</packing>
</child>
<child>
- <object class="GtkRadioButton" id="rb_bayes_evaluator">
+ <object class="GtkRadioButton" id="rb_grouped_evaluator">
<property name="label" translatable="yes">Grouped Evaluator</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -788,6 +945,7 @@ CSV
<property name="label" translatable="yes">Encryption</property>
<property name="use_underline">True</property>
<property name="icon_name">dialog-password</property>
+ <signal name="clicked" handler="_on_encryption_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -832,6 +990,7 @@ CSV
<property name="label" translatable="yes">User Mode</property>
<property name="use_underline">True</property>
<property name="icon_name">applications-internet</property>
+ <signal name="clicked" handler="_on_user_mode_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@@ -1349,4 +1508,113 @@ CSV
<action-widget response="0">btn_rules_close</action-widget>
</action-widgets>
</object>
+ <object class="GtkDialog" id="user_window">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="type">popup</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="type_hint">dialog</property>
+ <property name="transient_for">main_window</property>
+ <property name="has_resize_grip">False</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox8">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area8">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="btn_do_it">
+ <property name="label" translatable="yes">Go!</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="_on_go_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="btn_user_close">
+ <property name="label" translatable="yes">Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="_on_user_close_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="box6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Insert provider in the field below and press go:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="te_user_provider">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">•</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="img_user">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-missing-image</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">btn_do_it</action-widget>
+ <action-widget response="-1">btn_user_close</action-widget>
+ </action-widgets>
+ </object>
</interface>
diff --git a/Src/PyCatcher/src/evaluators.py b/Src/PyCatcher/src/evaluators.py
index 719f996..759d400 100644
--- a/Src/PyCatcher/src/evaluators.py
+++ b/Src/PyCatcher/src/evaluators.py
@@ -1,14 +1,10 @@
from rules import RuleResult
+from settings import Rule_Groups, Rule_Weights
class EvaluatorSelect:
CONSERVATIVE = 0
WEIGHTED = 1
- BAYES = 2
- MACHINE = 3
-
-class StationClass:
- BASE_STATION = 0
- CATCHER = 1
+ GROUP = 2
class Evaluator:
@@ -35,11 +31,47 @@ class ConservativeEvaluator(Evaluator):
break
return final_result, {'Decision founded on': decision_rule}
-class BayesEvaluator(Evaluator):
- return_type = type(int)
+
class WeightedEvaluator(Evaluator):
- return_type = type(int)
+ identifier = 'Weighted Evaluator'
+
+ def evaluate(self, result_list):
+ for rule, evaluation in reseult_list:
+ pass
+
+
+
+class GroupEvaluator(Evaluator):
+ identifier = 'Group Evaluator'
+
+ def evaluate(self, result_list):
+ group_results = []
+ for group in Rule_Groups:
+ group_results.append(self.evaluate_group_results(self.convert_to_group_result_list(group,result_list)))
+
+ if group_results.count(RuleResult.CRITICAL) > 0:
+ return RuleResult.CRITICAL
+ elif group_results.count(RuleResult.WARNING) > 0:
+ return RuleResult.WARNING
+ else:
+ return RuleResult.OK
+
+ def convert_to_group_result_list(self, group, result_list):
+ group_result_list = []
+ for rule in group:
+ group_results.append(result_list[rule])
+ return group_result_list
-class MachineLearningEvaluator(Evaluator):
- return_type = type(StationClass) \ No newline at end of file
+ def evaluate_group_results(self, results):
+ oks = results.count(RuleResult.OK)
+ warnings = results.count(RuleResult.WARNING)
+ criticals = results.count(RuleResult.CRITICAL)
+ if criticals >= oks and criticals >= warnings:
+ return RuleResult.CRITICAL
+ elif warnings >= oks and warnings>= criticals:
+ return RuleResult.WARNING
+ elif oks >= criticals and oks >= warnings:
+ return RuleResult.OK
+ else:
+ return RuleResult.IGNORE \ No newline at end of file
diff --git a/Src/PyCatcher/src/pyCatcherController.py b/Src/PyCatcher/src/pyCatcherController.py
index f964583..38f1567 100644
--- a/Src/PyCatcher/src/pyCatcherController.py
+++ b/Src/PyCatcher/src/pyCatcherController.py
@@ -5,7 +5,7 @@ from driverConnector import DriverConnector
from pyCatcherModel import BaseStationInformation, BaseStationInformationList
from pyCatcherView import PyCatcherGUI
from filters import ARFCNFilter,ProviderFilter
-from evaluators import EvaluatorSelect, BayesEvaluator, ConservativeEvaluator, WeightedEvaluator
+from evaluators import EvaluatorSelect, ConservativeEvaluator, WeightedEvaluator, GroupEvaluator
from rules import ProviderRule, ARFCNMappingRule, CountryMappingRule, LACMappingRule, UniqueCellIDRule, \
LACMedianRule, NeighbourhoodStructureRule, PureNeighbourhoodRule, FullyDiscoveredNeighbourhoodsRule, RuleResult, CellIDDatabaseRule, LocationAreaDatabaseRule, RxChangeRule, LACChangeRule
import pickle
@@ -32,7 +32,7 @@ class PyCatcherController:
self._cell_id_database = CellIDDatabase()
self._conservative_evaluator = ConservativeEvaluator()
- self._bayes_evaluator = BayesEvaluator()
+ self._group_evaluator = GroupEvaluator()
self._weighted_evaluator = WeightedEvaluator()
self._active_evaluator = self._conservative_evaluator
@@ -118,10 +118,17 @@ class PyCatcherController:
def set_evaluator (self, evaluator):
if evaluator == EvaluatorSelect.CONSERVATIVE:
self._active_evaluator = self._conservative_evaluator
- elif evaluator == EvaluatorSelect.BAYES:
- self._active_evaluator = self._bayes_evaluator
+ elif evaluator == EvaluatorSelect.GROUP:
+ self._active_evaluator = self._group_evaluator
elif evaluator == EvaluatorSelect.WEIGHTED:
self._active_evaluator = self._weighted_evaluator
+ self.trigger_evaluation()
+
+ def user_go(self, provider):
+ pass
+
+ def scan_encryption(self, arfcn_list, timeout):
+ pass
def update_with_web_services(self):
self._gui.log_line('Starting online lookups...')
@@ -211,7 +218,7 @@ class PyCatcherController:
elif item.evaluation == 'Critical':
result = RuleResult.CRITICAL
break
- self._gui.set_image(result)
+ self._gui.set_evaluator_image(result)
def export_csv(self):
if self._location == '':
diff --git a/Src/PyCatcher/src/pyCatcherView.py b/Src/PyCatcher/src/pyCatcherView.py
index 3c3c961..196a650 100644
--- a/Src/PyCatcher/src/pyCatcherView.py
+++ b/Src/PyCatcher/src/pyCatcherView.py
@@ -5,6 +5,7 @@ from xdot import DotWidget
import datetime
import time
from rules import RuleResult
+from evaluators import EvaluatorSelect
class PyCatcherGUI:
@@ -23,13 +24,18 @@ class PyCatcherGUI:
self._evaluators_window = self._builder.get_object('evaluators_window')
self._evaluation_window = self._builder.get_object('evaluation_window')
self._evaluation_image = self._builder.get_object('evaluation_image')
+ self._user_image = self._builder.get_object('img_user')
self._databases_window = self._builder.get_object('databases_window')
+ self._encryption_window = self._builder.get_object('encryption_window')
+ self._user_window = self._builder.get_object('user_window')
+
self._ok_image = gtk.gdk.pixbuf_new_from_file('../GUI/Images/ok.png')
self._warning_image = gtk.gdk.pixbuf_new_from_file('../GUI/Images/warning.png')
self._critical_image = gtk.gdk.pixbuf_new_from_file('../GUI/Images/critical.png')
self._plain_image = gtk.gdk.pixbuf_new_from_file('../GUI/Images/plain.png')
- self.set_image(RuleResult.IGNORE)
+ self.set_evaluator_image(RuleResult.IGNORE)
+ self.set_user_image(RuleResult.IGNORE)
self._catcher_controller = catcher_controller
@@ -61,7 +67,7 @@ class PyCatcherGUI:
self._main_window.show()
- def set_image(self, status):
+ def set_evaluator_image(self, status):
pixbuf = self._plain_image
if status == RuleResult.OK:
pixbuf = self._ok_image
@@ -72,7 +78,21 @@ class PyCatcherGUI:
width, height = self._evaluation_window.get_size()
pixbuf = pixbuf.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR)
self._evaluation_image.set_from_pixbuf(pixbuf)
-
+
+ def set_user_image(self, status):
+ pixbuf = self._plain_image
+ if status == RuleResult.OK:
+ pixbuf = self._ok_image
+ elif status == RuleResult.WARNING:
+ pixbuf = self._warning_image
+ elif status == RuleResult.CRITICAL:
+ pixbuf = self._critical_image
+ elif status == RuleResult.IGNORE:
+ pixbuf = self._plain_image
+ pixbuf = pixbuf.scale_simple(320, 240, gtk.gdk.INTERP_BILINEAR)
+ self._user_image.set_from_pixbuf(pixbuf)
+
+
def _add_column(self, name, index):
column = gtk.TreeViewColumn(name, gtk.CellRendererText(), text=index)
column.set_resizable(True)
@@ -112,7 +132,12 @@ class PyCatcherGUI:
self._catcher_controller.trigger_evaluation()
def _update_evaluators(self):
- pass
+ if self._builder.get_object('rb_conservative_evaluator').get_active():
+ self._catcher_controller.set_evaluator(EvaluatorSelect.CONSERVATIVE)
+ elif self._builder.get_object('rb_weighted_evaluator').get_active():
+ self._catcher_controller.set_evaluator(EvaluatorSelect.WEIGHTED)
+ elif self._builder.get_object('rb_grouped_evaluator').get_active():
+ self._catcher_controller.set_evaluator(EvaluatorSelect.GROUP)
def _on_csv_clicked(self, widget):
self._update_databases()
@@ -140,9 +165,32 @@ class PyCatcherGUI:
def log_line(self, line):
self._log_buffer.insert(self._log_buffer.get_end_iter(),self._utf8conv(datetime.datetime.now().strftime("%I:%M:%S %p")+ ": " + line + "\n"))
- def _on_graph_node_clicked (self, widget, url, event):
+ def _on_graph_node_clicked(self, widget, url, event):
print 'NODE CLICKED'
+ def _on_user_close_clicked(self, widget):
+ self._user_window.hide()
+
+ def _on_encryption_close_clicked(self, widget):
+ self._catcher_controller.trigger_evaluation()
+ self._encryption_window.hide()
+
+ def _on_scan_enc_clicked(self, widget):
+ arfcn_list = map(int,(self._builder.get_object('te_enc_arfcns').get_text()).split(','))
+ timeout = int(self._builder.get_object('te_enc_timeout').get_text())
+ result = self._catcher_controller.scan_encryption(arfcn_list, timeout)
+ self._builder.get_object('lbl_enc_result').set_text('Result: ' + result)
+ def _on_go_clicked(self, widget):
+ provider = self._builder.get_object('te_user_provider').get_text()
+ result = self._catcher_controller.user_go()
+ self.set_user_image(result)
+
+ def _on_user_mode_clicked(self, widget):
+ self._user_window.show()
+
+ def _on_encryption_clicked(self, widget):
+ self._encryption_window.show()
+
def _on_web_services_clicked(self, widget):
self._update_databases()
self._catcher_controller.update_with_web_services()
diff --git a/Src/PyCatcher/src/settings.py b/Src/PyCatcher/src/settings.py
index 831fac6..e62e5c5 100644
--- a/Src/PyCatcher/src/settings.py
+++ b/Src/PyCatcher/src/settings.py
@@ -52,6 +52,12 @@ DB_RX_threshold = 0.05
CH_RX_threshold = 0.02
+#Evaluator Configuration ---------------------------------------------------------------------------------------
+
+Rule_Groups = []
+
+Rule_Weights = {}
+
#Database Configuration ----------------------------------------------------------------------------------------
Open_Cell_ID_Key = 'd7a5bc3f21b44d4bf93d1ec2b3f83dc4'
diff --git a/Src/osmolib/src/host/layer23/src/misc/Makefile.am b/Src/osmolib/src/host/layer23/src/misc/Makefile.am
index 1c5bd5c..8e9d7f6 100644
--- a/Src/osmolib/src/host/layer23/src/misc/Makefile.am
+++ b/Src/osmolib/src/host/layer23/src/misc/Makefile.am
@@ -2,11 +2,12 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS)
LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS)
-bin_PROGRAMS = bcch_scan ccch_scan echo_test cell_log catcher cbch_sniff
+bin_PROGRAMS = bcch_scan ccch_scan echo_test enc_scan cell_log catcher cbch_sniff
bcch_scan_SOURCES = ../common/main.c app_bcch_scan.c bcch_scan.c
ccch_scan_SOURCES = ../common/main.c app_ccch_scan.c rslms.c
echo_test_SOURCES = ../common/main.c app_echo_test.c
+enc_scan_SOURCES = ../common/main.c enc_scan.c rslms.c
cell_log_LDADD = $(LDADD) -lm
cell_log_SOURCES = ../common/main.c app_cell_log.c cell_log.c \
../../../gsmmap/geo.c
diff --git a/Src/osmolib/src/host/layer23/src/misc/enc_scan b/Src/osmolib/src/host/layer23/src/misc/enc_scan
new file mode 100755
index 0000000..3b26c35
--- /dev/null
+++ b/Src/osmolib/src/host/layer23/src/misc/enc_scan
Binary files differ
diff --git a/Src/osmolib/src/host/layer23/src/misc/enc_scan.c b/Src/osmolib/src/host/layer23/src/misc/enc_scan.c
new file mode 100644
index 0000000..bfba4ed
--- /dev/null
+++ b/Src/osmolib/src/host/layer23/src/misc/enc_scan.c
@@ -0,0 +1,556 @@
+/* CCCH passive sniffer */
+/* (C) 2010-2011 by Holger Hans Peter Freyther
+ * (C) 2010 by Harald Welte <laforge@gnumonks.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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/misc/rslms.h>
+#include <osmocom/bb/misc/layer3.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/l23_app.h>
+
+#include <l1ctl_proto.h>
+
+static struct {
+ int has_si1;
+ int ccch_mode;
+ int ccch_enabled;
+ int rach_count;
+ struct gsm_sysinfo_freq cell_arfcns[1024];
+} app_state;
+
+
+static void dump_bcch(struct osmocom_ms *ms, uint8_t tc, const uint8_t *data)
+{
+ struct gsm48_system_information_type_header *si_hdr;
+ si_hdr = (struct gsm48_system_information_type_header *) data;
+
+ /* GSM 05.02 §6.3.1.3 Mapping of BCCH data */
+ switch (si_hdr->system_information) {
+ case GSM48_MT_RR_SYSINFO_1:
+#ifdef BCCH_TC_CHECK
+ if (tc != 0)
+ //LOGP(DRR, LOGL_ERROR, "SI1 on the wrong TC: %d\n", tc);
+#endif
+ if (!app_state.has_si1) {
+ struct gsm48_system_information_type_1 *si1 =
+ (struct gsm48_system_information_type_1 *)data;
+
+ gsm48_decode_freq_list(app_state.cell_arfcns,
+ si1->cell_channel_description,
+ sizeof(si1->cell_channel_description),
+ 0xff, 0x01);
+
+ app_state.has_si1 = 1;
+ //LOGP(DRR, LOGL_ERROR, "SI1 received.\n");
+ }
+ break;
+ case GSM48_MT_RR_SYSINFO_2:
+#ifdef BCCH_TC_CHECK
+ if (tc != 1)
+ //LOGP(DRR, LOGL_ERROR, "SI2 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_3:
+#ifdef BCCH_TC_CHECK
+ if (tc != 2 && tc != 6)
+ //LOGP(DRR, LOGL_ERROR, "SI3 on the wrong TC: %d\n", tc);
+#endif
+ if (app_state.ccch_mode == CCCH_MODE_NONE) {
+ struct gsm48_system_information_type_3 *si3 =
+ (struct gsm48_system_information_type_3 *)data;
+
+ if (si3->control_channel_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
+ app_state.ccch_mode = CCCH_MODE_COMBINED;
+ else
+ app_state.ccch_mode = CCCH_MODE_NON_COMBINED;
+
+ l1ctl_tx_ccch_mode_req(ms, app_state.ccch_mode);
+ }
+ break;
+ case GSM48_MT_RR_SYSINFO_4:
+#ifdef BCCH_TC_CHECK
+ if (tc != 3 && tc != 7)
+ //LOGP(DRR, LOGL_ERROR, "SI4 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_5:
+ break;
+ case GSM48_MT_RR_SYSINFO_6:
+ break;
+ case GSM48_MT_RR_SYSINFO_7:
+#ifdef BCCH_TC_CHECK
+ if (tc != 7)
+ //LOGP(DRR, LOGL_ERROR, "SI7 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_8:
+#ifdef BCCH_TC_CHECK
+ if (tc != 3)
+ //LOGP(DRR, LOGL_ERROR, "SI8 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_9:
+#ifdef BCCH_TC_CHECK
+ if (tc != 4)
+ //LOGP(DRR, LOGL_ERROR, "SI9 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_13:
+#ifdef BCCH_TC_CHECK
+ if (tc != 4 && tc != 0)
+ //LOGP(DRR, LOGL_ERROR, "SI13 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_16:
+#ifdef BCCH_TC_CHECK
+ if (tc != 6)
+ //LOGP(DRR, LOGL_ERROR, "SI16 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_17:
+#ifdef BCCH_TC_CHECK
+ if (tc != 2)
+ //LOGP(DRR, LOGL_ERROR, "SI17 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_2bis:
+#ifdef BCCH_TC_CHECK
+ if (tc != 5)
+ //LOGP(DRR, LOGL_ERROR, "SI2bis on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_2ter:
+#ifdef BCCH_TC_CHECK
+ if (tc != 5 && tc != 4)
+ //LOGP(DRR, LOGL_ERROR, "SI2ter on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_5bis:
+ break;
+ case GSM48_MT_RR_SYSINFO_5ter:
+ break;
+ default:
+ /*
+ LOGP(DRR, LOGL_ERROR, "Unknown SI: %d\n",
+ si_hdr->system_information);
+ */
+ break;
+ };
+}
+
+/* send location updating request * (as part of RSLms EST IND /
+ LAPDm SABME) */
+
+static int gsm48_tx_loc_upd_req(struct osmocom_ms *ms, uint8_t chan_nr)
+{
+ LOGP(DRR,LOGL_NOTICE, "tx-loc-upd-req");
+ struct msgb *msg = msgb_alloc_headroom(256, 16, "loc_upd_req");
+ struct gsm48_hdr *gh;
+ struct gsm48_loc_upd_req *lu_r;
+
+ //DEBUGP(DMM, "chan_nr=%u\n", chan_nr);
+
+ msg->l3h = msgb_put(msg, sizeof(*gh));
+ gh = (struct gsm48_hdr *) msg->l3h;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST;
+ lu_r = (struct gsm48_loc_upd_req *) msgb_put(msg, sizeof(*lu_r));
+ lu_r->type = GSM48_LUPD_IMSI_ATT;
+ lu_r->key_seq = 0;
+ /* FIXME: set LAI and CM1 */
+ /* FIXME: set MI */
+
+ lu_r->mi_len = 0;
+
+ return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, chan_nr, 0, msg);
+}
+
+
+/**
+ * This method used to send a l1ctl_tx_dm_est_req_h0 or
+ * a l1ctl_tx_dm_est_req_h1 to the layer1 to follow this
+ * assignment. The code has been removed.
+ */
+static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_imm_ass *ia = msgb_l3(msg);
+ uint8_t ch_type, ch_subch, ch_ts;
+
+ //needed to go to dedicated mode
+ int rv;
+
+ /* Discard packet TBF assignement */
+ if (ia->page_mode & 0xf0)
+ return 0;
+
+ /* FIXME: compare RA and GSM time with when we sent RACH req */
+
+ rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts);
+
+ if (!ia->chan_desc.h0.h) {
+ /* Non-hopping */
+ uint16_t arfcn;
+
+ arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8);
+
+ LOGP(DRR, LOGL_NOTICE, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
+ "ARFCN=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra,
+ ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch,
+ ia->chan_desc.h0.tsc);
+
+ /* request L1 to go to dedicated mode on assigned channel */
+
+ rv = l1ctl_tx_dm_est_req_h0(ms,
+ arfcn, ia->chan_desc.chan_nr, ia->chan_desc.h0.tsc,
+ GSM48_CMODE_SIGN,0);
+
+
+ } else {
+ /* Hopping */
+ uint8_t maio, hsn, ma_len;
+ uint16_t ma[64], arfcn;
+ int i, j, k;
+
+ hsn = ia->chan_desc.h1.hsn;
+ maio = ia->chan_desc.h1.maio_low | (ia->chan_desc.h1.maio_high << 2);
+
+ LOGP(DRR, LOGL_NOTICE, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
+ "HSN=%u, MAIO=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra,
+ ia->chan_desc.chan_nr, hsn, maio, ch_ts, ch_subch,
+ ia->chan_desc.h1.tsc);
+
+ /* decode mobile allocation */
+ ma_len = 0;
+ for (i=1, j=0; i<=1024; i++) {
+ arfcn = i & 1023;
+ if (app_state.cell_arfcns[arfcn].mask & 0x01) {
+ k = ia->mob_alloc_len - (j>>3) - 1;
+ if (ia->mob_alloc[k] & (1 << (j&7))) {
+ ma[ma_len++] = arfcn;
+ }
+ j++;
+ }
+ }
+ /* request L1 to go to dedicated mode on assigned channel */
+
+ rv = l1ctl_tx_dm_est_req_h1(ms,
+ maio, hsn, ma, ma_len,
+ ia->chan_desc.chan_nr, ia->chan_desc.h1.tsc,
+ GSM48_CMODE_SIGN,0);
+
+ }
+
+ LOGPC(DRR, LOGL_NOTICE, "\n");
+
+ rv = gsm48_tx_loc_upd_req(ms, ia->chan_desc.chan_nr);
+
+ return rv;
+ return 0;
+}
+
+static const char *pag_print_mode(int mode)
+{
+ switch (mode) {
+ case 0:
+ return "Normal paging";
+ case 1:
+ return "Extended paging";
+ case 2:
+ return "Paging reorganization";
+ case 3:
+ return "Same as before";
+ default:
+ return "invalid";
+ }
+}
+
+static char *chan_need(int need)
+{
+ switch (need) {
+ case 0:
+ return "any";
+ case 1:
+ return "sdch";
+ case 2:
+ return "tch/f";
+ case 3:
+ return "tch/h";
+ default:
+ return "invalid";
+ }
+}
+
+static char *mi_type_to_string(int type)
+{
+ switch (type) {
+ case GSM_MI_TYPE_NONE:
+ return "none";
+ case GSM_MI_TYPE_IMSI:
+ return "imsi";
+ case GSM_MI_TYPE_IMEI:
+ return "imei";
+ case GSM_MI_TYPE_IMEISV:
+ return "imeisv";
+ case GSM_MI_TYPE_TMSI:
+ return "tmsi";
+ default:
+ return "invalid";
+ }
+}
+
+/**
+ * This can contain two MIs. The size checking is a bit of a mess.
+ */
+static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_paging1 *pag;
+ int len1, len2, mi_type, tag;
+ char mi_string[GSM48_MI_SIZE];
+
+ /* is there enough room for the header + LV? */
+ if (msgb_l3len(msg) < sizeof(*pag) + 2) {
+ //LOGP(DRR, LOGL_ERROR, "PagingRequest is too short.\n");
+ return -1;
+ }
+
+ pag = msgb_l3(msg);
+ len1 = pag->data[0];
+ mi_type = pag->data[1] & GSM_MI_TYPE_MASK;
+
+ if (msgb_l3len(msg) < sizeof(*pag) + 2 + len1) {
+ //LOGP(DRR, LOGL_ERROR, "PagingRequest with wrong MI\n");
+ return -1;
+ }
+
+ if (mi_type != GSM_MI_TYPE_NONE) {
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[1], len1);
+ LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to %s M(%s) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed1),
+ mi_type_to_string(mi_type),
+ mi_string);
+ }
+
+ /* check if we have a MI type in here */
+ if (msgb_l3len(msg) < sizeof(*pag) + 2 + len1 + 3)
+ return 0;
+
+ tag = pag->data[2 + len1 + 0];
+ len2 = pag->data[2 + len1 + 1];
+ mi_type = pag->data[2 + len1 + 2] & GSM_MI_TYPE_MASK;
+ if (tag == GSM48_IE_MOBILE_ID && mi_type != GSM_MI_TYPE_NONE) {
+ if (msgb_l3len(msg) < sizeof(*pag) + 2 + len1 + 3 + len2) {
+ //LOGP(DRR, LOGL_ERROR, "Optional MI does not fit here.\n");
+ return -1;
+ }
+
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[2 + len1 + 2], len2);
+ LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to %s M(%s) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed2),
+ mi_type_to_string(mi_type),
+ mi_string);
+ }
+ return 0;
+}
+
+static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_paging2 *pag;
+ int tag, len, mi_type;
+ char mi_string[GSM48_MI_SIZE];
+
+ if (msgb_l3len(msg) < sizeof(*pag)) {
+ //LOGP(DRR, LOGL_ERROR, "Paging2 message is too small.\n");
+ return -1;
+ }
+
+ pag = msgb_l3(msg);
+ LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to TMSI M(0x%x) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed1), pag->tmsi1);
+ LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to TMSI M(0x%x) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed2), pag->tmsi2);
+ /* no optional element */
+ if (msgb_l3len(msg) < sizeof(*pag) + 3)
+ return 0;
+
+ tag = pag->data[0];
+ len = pag->data[1];
+ mi_type = pag->data[2] & GSM_MI_TYPE_MASK;
+
+ if (tag != GSM48_IE_MOBILE_ID)
+ return 0;
+
+ if (msgb_l3len(msg) < sizeof(*pag) + 3 + len) {
+ //LOGP(DRR, LOGL_ERROR, "Optional MI does not fit in here\n");
+ return -1;
+ }
+
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[2], len);
+ LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan %s to %s M(%s) \n",
+ pag_print_mode(pag->pag_mode),
+ "n/a ",
+ mi_type_to_string(mi_type),
+ mi_string);
+ return 0;
+}
+
+static int gsm48_rx_paging_p3(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_paging3 *pag;
+
+ if (msgb_l3len(msg) < sizeof(*pag)) {
+ //LOGP(DRR, LOGL_ERROR, "Paging3 message is too small.\n");
+ return -1;
+ }
+
+ pag = msgb_l3(msg);
+ LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to TMSI M(0x%x) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed1), pag->tmsi1);
+ LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to TMSI M(0x%x) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed2), pag->tmsi2);
+ LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan %s to TMSI M(0x%x) \n",
+ pag_print_mode(pag->pag_mode),
+ "n/a ", pag->tmsi3);
+ LOGP(DRR, LOGL_NOTICE, "Paging4: %s chan %s to TMSI M(0x%x) \n",
+ pag_print_mode(pag->pag_mode),
+ "n/a ", pag->tmsi4);
+ return 0;
+}
+
+int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+ int rc = 0;
+
+ //if (sih->rr_protocol_discriminator != GSM48_PDISC_RR)
+ //LOGP(DRR, LOGL_ERROR, "PCH pdisc != RR\n");
+
+ switch (sih->system_information) {
+ case GSM48_MT_RR_PAG_REQ_1:
+ gsm48_rx_paging_p1(msg, ms);
+ break;
+ case GSM48_MT_RR_PAG_REQ_2:
+ gsm48_rx_paging_p2(msg, ms);
+ break;
+ case GSM48_MT_RR_PAG_REQ_3:
+ gsm48_rx_paging_p3(msg, ms);
+ break;
+ case GSM48_MT_RR_IMM_ASS:
+ gsm48_rx_imm_ass(msg, ms);
+ break;
+ case GSM48_MT_RR_NOTIF_NCH:
+ /* notification for voice call groups and such */
+ break;
+ case 0x07:
+ /* wireshark know that this is SI2 quater and for 3G interop */
+ break;
+ default:
+ LOGP(DRR, LOGL_NOTICE, "unknown PCH/AGCH type 0x%02x\n",
+ sih->system_information);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms)
+{
+ /* FIXME: we have lost the gsm frame time until here, need to store it
+ * in some msgb context */
+ //dump_bcch(dl->time.tc, ccch->data);
+ dump_bcch(ms, 0, msg->l3h);
+
+ /* Req channel logic */
+ if (app_state.ccch_enabled && (app_state.rach_count < 2)) {
+ l1ctl_tx_rach_req(ms, app_state.rach_count, 0,
+ app_state.ccch_mode == CCCH_MODE_COMBINED);
+ app_state.rach_count++;
+ }
+
+ return 0;
+}
+
+void layer3_app_reset(void)
+{
+ /* Reset state */
+ app_state.has_si1 = 0;
+ app_state.ccch_mode = CCCH_MODE_NONE;
+ app_state.ccch_enabled = 0;
+ app_state.rach_count = 0;
+
+ memset(&app_state.cell_arfcns, 0x00, sizeof(app_state.cell_arfcns));
+}
+
+static int signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmocom_ms *ms;
+
+ if (subsys != SS_L1CTL)
+ return 0;
+
+ switch (signal) {
+ case S_L1CTL_RESET:
+ ms = signal_data;
+ layer3_app_reset();
+ return l1ctl_tx_fbsb_req(ms, ms->test_arfcn,
+ L1CTL_FBSB_F_FB01SB, 100, 0,
+ CCCH_MODE_NONE);
+ break;
+ }
+ return 0;
+}
+
+
+int l23_app_init(struct osmocom_ms *ms)
+{
+ osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL);
+ l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
+ return layer3_init(ms);
+}
+
+static struct l23_app_info info = {
+ .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n",
+ .contribution = "Contributions by Holger Hans Peter Freyther\n",
+};
+
+struct l23_app_info *l23_app_info()
+{
+ return &info;
+}
diff --git a/Src/osmolib/src/host/layer23/src/misc/enc_scan_old.c b/Src/osmolib/src/host/layer23/src/misc/enc_scan_old.c
new file mode 100644
index 0000000..1c097df
--- /dev/null
+++ b/Src/osmolib/src/host/layer23/src/misc/enc_scan_old.c
@@ -0,0 +1,785 @@
+/* CCCH passive sniffer */
+/* (C) 2010-2011 by Holger Hans Peter Freyther
+ * (C) 2010 by Harald Welte <laforge@gnumonks.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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <getopt.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/gsm/a5.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/misc/rslms.h>
+#include <osmocom/bb/misc/layer3.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/l23_app.h>
+
+#include <l1ctl_proto.h>
+
+#include <osmocom/bb/misc/xcch.h>
+
+extern struct gsmtap_inst *gsmtap_inst;
+
+enum dch_state_t {
+ DCH_NONE,
+ DCH_WAIT_EST,
+ DCH_ACTIVE,
+ DCH_WAIT_REL,
+};
+
+static struct {
+ int has_si1;
+ int ccch_mode;
+
+ enum dch_state_t dch_state;
+ uint8_t dch_nr;
+ int dch_badcnt;
+ int dch_ciph;
+
+ FILE * fh;
+
+ sbit_t bursts_dl[116 * 4];
+ sbit_t bursts_ul[116 * 4];
+
+ struct gsm_sysinfo_freq cell_arfcns[1024];
+
+ uint8_t kc[8];
+} app_state;
+
+
+static void dump_bcch(struct osmocom_ms *ms, uint8_t tc, const uint8_t *data)
+{
+ struct gsm48_system_information_type_header *si_hdr;
+ si_hdr = (struct gsm48_system_information_type_header *) data;
+
+ /* GSM 05.02 §6.3.1.3 Mapping of BCCH data */
+ switch (si_hdr->system_information) {
+ case GSM48_MT_RR_SYSINFO_1:
+#ifdef BCCH_TC_CHECK
+ if (tc != 0)
+ LOGP(DRR, LOGL_ERROR, "SI1 on the wrong TC: %d\n", tc);
+#endif
+ if (!app_state.has_si1) {
+ struct gsm48_system_information_type_1 *si1 =
+ (struct gsm48_system_information_type_1 *)data;
+
+ gsm48_decode_freq_list(app_state.cell_arfcns,
+ si1->cell_channel_description,
+ sizeof(si1->cell_channel_description),
+ 0xff, 0x01);
+
+ app_state.has_si1 = 1;
+ LOGP(DRR, LOGL_ERROR, "SI1 received.\n");
+ }
+ break;
+ case GSM48_MT_RR_SYSINFO_2:
+#ifdef BCCH_TC_CHECK
+ if (tc != 1)
+ LOGP(DRR, LOGL_ERROR, "SI2 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_3:
+#ifdef BCCH_TC_CHECK
+ if (tc != 2 && tc != 6)
+ LOGP(DRR, LOGL_ERROR, "SI3 on the wrong TC: %d\n", tc);
+#endif
+ if (app_state.ccch_mode == CCCH_MODE_NONE) {
+ struct gsm48_system_information_type_3 *si3 =
+ (struct gsm48_system_information_type_3 *)data;
+
+ if (si3->control_channel_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
+ app_state.ccch_mode = CCCH_MODE_COMBINED;
+ else
+ app_state.ccch_mode = CCCH_MODE_NON_COMBINED;
+
+ l1ctl_tx_ccch_mode_req(ms, app_state.ccch_mode);
+ }
+ break;
+ case GSM48_MT_RR_SYSINFO_4:
+#ifdef BCCH_TC_CHECK
+ if (tc != 3 && tc != 7)
+ LOGP(DRR, LOGL_ERROR, "SI4 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_5:
+ break;
+ case GSM48_MT_RR_SYSINFO_6:
+ break;
+ case GSM48_MT_RR_SYSINFO_7:
+#ifdef BCCH_TC_CHECK
+ if (tc != 7)
+ LOGP(DRR, LOGL_ERROR, "SI7 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_8:
+#ifdef BCCH_TC_CHECK
+ if (tc != 3)
+ LOGP(DRR, LOGL_ERROR, "SI8 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_9:
+#ifdef BCCH_TC_CHECK
+ if (tc != 4)
+ LOGP(DRR, LOGL_ERROR, "SI9 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_13:
+#ifdef BCCH_TC_CHECK
+ if (tc != 4 && tc != 0)
+ LOGP(DRR, LOGL_ERROR, "SI13 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_16:
+#ifdef BCCH_TC_CHECK
+ if (tc != 6)
+ LOGP(DRR, LOGL_ERROR, "SI16 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_17:
+#ifdef BCCH_TC_CHECK
+ if (tc != 2)
+ LOGP(DRR, LOGL_ERROR, "SI17 on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_2bis:
+#ifdef BCCH_TC_CHECK
+ if (tc != 5)
+ LOGP(DRR, LOGL_ERROR, "SI2bis on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_2ter:
+#ifdef BCCH_TC_CHECK
+ if (tc != 5 && tc != 4)
+ LOGP(DRR, LOGL_ERROR, "SI2ter on the wrong TC: %d\n", tc);
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_5bis:
+ break;
+ case GSM48_MT_RR_SYSINFO_5ter:
+ break;
+ default:
+ fprintf(stderr, "\tUnknown SI");
+ break;
+ };
+}
+
+
+/**
+ * This method used to send a l1ctl_tx_dm_est_req_h0 or
+ * a l1ctl_tx_dm_est_req_h1 to the layer1 to follow this
+ * assignment. The code has been removed.
+ */
+static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_imm_ass *ia = msgb_l3(msg);
+ uint8_t ch_type, ch_subch, ch_ts;
+ int rv;
+
+ /* Discard packet TBF assignement */
+ if (ia->page_mode & 0xf0)
+ return 0;
+
+ /* If we're not ready yet, or just busy ... */
+ if ((!app_state.has_si1) || (app_state.dch_state != DCH_NONE))
+ return 0;
+
+ rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts);
+
+ if (!ia->chan_desc.h0.h) {
+ /* Non-hopping */
+ uint16_t arfcn;
+
+ arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8);
+
+ LOGP(DRR, LOGL_NOTICE, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
+ "ARFCN=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra,
+ ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch,
+ ia->chan_desc.h0.tsc);
+
+ /* request L1 to go to dedicated mode on assigned channel */
+ rv = l1ctl_tx_dm_est_req_h0(ms,
+ arfcn, ia->chan_desc.chan_nr, ia->chan_desc.h0.tsc,
+ GSM48_CMODE_SIGN, 0);
+ } else {
+ /* Hopping */
+ uint8_t maio, hsn, ma_len;
+ uint16_t ma[64], arfcn;
+ int i, j, k;
+
+ hsn = ia->chan_desc.h1.hsn;
+ maio = ia->chan_desc.h1.maio_low | (ia->chan_desc.h1.maio_high << 2);
+
+ LOGP(DRR, LOGL_NOTICE, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
+ "HSN=%u, MAIO=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra,
+ ia->chan_desc.chan_nr, hsn, maio, ch_ts, ch_subch,
+ ia->chan_desc.h1.tsc);
+
+ /* decode mobile allocation */
+ ma_len = 0;
+ for (i=1, j=0; i<=1024; i++) {
+ arfcn = i & 1023;
+ if (app_state.cell_arfcns[arfcn].mask & 0x01) {
+ k = ia->mob_alloc_len - (j>>3) - 1;
+ if (ia->mob_alloc[k] & (1 << (j&7))) {
+ ma[ma_len++] = arfcn;
+ }
+ j++;
+ }
+ }
+
+ /* request L1 to go to dedicated mode on assigned channel */
+ rv = l1ctl_tx_dm_est_req_h1(ms,
+ maio, hsn, ma, ma_len,
+ ia->chan_desc.chan_nr, ia->chan_desc.h1.tsc,
+ GSM48_CMODE_SIGN, 0);
+ }
+
+ LOGPC(DRR, LOGL_NOTICE, "\n");
+
+ /* Set state */
+ app_state.dch_state = DCH_WAIT_EST;
+ app_state.dch_nr = ia->chan_desc.chan_nr;
+ app_state.dch_badcnt = 0;
+
+ return rv;
+}
+
+static const char *pag_print_mode(int mode)
+{
+ switch (mode) {
+ case 0:
+ return "Normal paging";
+ case 1:
+ return "Extended paging";
+ case 2:
+ return "Paging reorganization";
+ case 3:
+ return "Same as before";
+ default:
+ return "invalid";
+ }
+}
+
+static char *chan_need(int need)
+{
+ switch (need) {
+ case 0:
+ return "any";
+ case 1:
+ return "sdch";
+ case 2:
+ return "tch/f";
+ case 3:
+ return "tch/h";
+ default:
+ return "invalid";
+ }
+}
+
+static char *mi_type_to_string(int type)
+{
+ switch (type) {
+ case GSM_MI_TYPE_NONE:
+ return "none";
+ case GSM_MI_TYPE_IMSI:
+ return "imsi";
+ case GSM_MI_TYPE_IMEI:
+ return "imei";
+ case GSM_MI_TYPE_IMEISV:
+ return "imeisv";
+ case GSM_MI_TYPE_TMSI:
+ return "tmsi";
+ default:
+ return "invalid";
+ }
+}
+
+/**
+ * This can contain two MIs. The size checking is a bit of a mess.
+ */
+static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_paging1 *pag;
+ int len1, len2, mi_type, tag;
+ char mi_string[GSM48_MI_SIZE];
+
+ /* is there enough room for the header + LV? */
+ if (msgb_l3len(msg) < sizeof(*pag) + 2) {
+ LOGP(DRR, LOGL_ERROR, "PagingRequest is too short.\n");
+ return -1;
+ }
+
+ pag = msgb_l3(msg);
+ len1 = pag->data[0];
+ mi_type = pag->data[1] & GSM_MI_TYPE_MASK;
+
+ if (msgb_l3len(msg) < sizeof(*pag) + 2 + len1) {
+ LOGP(DRR, LOGL_ERROR, "PagingRequest with wrong MI\n");
+ return -1;
+ }
+
+ if (mi_type != GSM_MI_TYPE_NONE) {
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[1], len1);
+ LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to %s M(%s) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed1),
+ mi_type_to_string(mi_type),
+ mi_string);
+ }
+
+ /* check if we have a MI type in here */
+ if (msgb_l3len(msg) < sizeof(*pag) + 2 + len1 + 3)
+ return 0;
+
+ tag = pag->data[2 + len1 + 0];
+ len2 = pag->data[2 + len1 + 1];
+ mi_type = pag->data[2 + len1 + 2] & GSM_MI_TYPE_MASK;
+ if (tag == GSM48_IE_MOBILE_ID && mi_type != GSM_MI_TYPE_NONE) {
+ if (msgb_l3len(msg) < sizeof(*pag) + 2 + len1 + 3 + len2) {
+ LOGP(DRR, LOGL_ERROR, "Optional MI does not fit here.\n");
+ return -1;
+ }
+
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[2 + len1 + 2], len2);
+ LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to %s M(%s) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed2),
+ mi_type_to_string(mi_type),
+ mi_string);
+ }
+ return 0;
+}
+
+static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_paging2 *pag;
+ int tag, len, mi_type;
+ char mi_string[GSM48_MI_SIZE];
+
+ if (msgb_l3len(msg) < sizeof(*pag)) {
+ LOGP(DRR, LOGL_ERROR, "Paging2 message is too small.\n");
+ return -1;
+ }
+
+ pag = msgb_l3(msg);
+ LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to TMSI M(0x%x) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed1), pag->tmsi1);
+ LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to TMSI M(0x%x) \n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed1), pag->tmsi2);
+
+ /* no optional element */
+ if (msgb_l3len(msg) < sizeof(*pag) + 3)
+ return 0;
+
+ tag = pag->data[0];
+ len = pag->data[1];
+ mi_type = pag->data[2] & GSM_MI_TYPE_MASK;
+
+ if (tag != GSM48_IE_MOBILE_ID)
+ return 0;
+
+ if (msgb_l3len(msg) < sizeof(*pag) + 3 + len) {
+ LOGP(DRR, LOGL_ERROR, "Optional MI does not fit in here\n");
+ return -1;
+ }
+
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[2], len);
+ LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan %s to %s M(%s) \n",
+ pag_print_mode(pag->pag_mode),
+ "n/a ",
+ mi_type_to_string(mi_type),
+ mi_string);
+
+ return 0;
+}
+
+int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+ int rc = 0;
+
+ /* CCCH marks the end of WAIT_REL */
+ if (app_state.dch_state == DCH_WAIT_REL)
+ app_state.dch_state = DCH_NONE;
+
+ if (sih->rr_protocol_discriminator != GSM48_PDISC_RR)
+ LOGP(DRR, LOGL_ERROR, "PCH pdisc != RR\n");
+
+ switch (sih->system_information) {
+ case GSM48_MT_RR_PAG_REQ_1:
+ gsm48_rx_paging_p1(msg, ms);
+ break;
+ case GSM48_MT_RR_PAG_REQ_2:
+ gsm48_rx_paging_p2(msg, ms);
+ break;
+ case GSM48_MT_RR_PAG_REQ_3:
+ LOGP(DRR, LOGL_ERROR, "PAGING of type 3 is not implemented.\n");
+ break;
+ case GSM48_MT_RR_IMM_ASS:
+ gsm48_rx_imm_ass(msg, ms);
+ break;
+ case GSM48_MT_RR_NOTIF_NCH:
+ /* notification for voice call groups and such */
+ break;
+ case 0x07:
+ /* wireshark know that this is SI2 quater and for 3G interop */
+ break;
+ default:
+ LOGP(DRR, LOGL_NOTICE, "unknown PCH/AGCH type 0x%02x\n",
+ sih->system_information);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms)
+{
+ /* BCCH marks the end of WAIT_REL */
+ if (app_state.dch_state == DCH_WAIT_REL)
+ app_state.dch_state = DCH_NONE;
+
+ /* FIXME: we have lost the gsm frame time until here, need to store it
+ * in some msgb context */
+ //dump_bcch(dl->time.tc, ccch->data);
+ dump_bcch(ms, 0, msg->l3h);
+
+ return 0;
+}
+
+
+static void local_burst_decode(struct l1ctl_burst_ind *bi)
+{
+ int16_t rx_dbm;
+ uint16_t arfcn;
+ uint32_t fn;
+ uint8_t cbits, tn, lch_idx;
+ int ul, bid, i;
+ sbit_t *bursts;
+ ubit_t bt[116];
+
+ /* Get params (Only for SDCCH and SACCH/{4,8,F,H}) */
+ arfcn = ntohs(bi->band_arfcn);
+ rx_dbm = rxlev2dbm(bi->rx_level);
+
+ fn = ntohl(bi->frame_nr);
+ ul = !!(arfcn & ARFCN_UPLINK);
+ bursts = ul ? app_state.bursts_ul : app_state.bursts_dl;
+
+ cbits = bi->chan_nr >> 3;
+ tn = bi->chan_nr & 7;
+
+ bid = -1;
+
+ if (cbits == 0x01) { /* TCH/F */
+ lch_idx = 0;
+ if (bi->flags & BI_FLG_SACCH) {
+ uint32_t fn_report;
+ fn_report = (fn - (tn * 13) + 104) % 104;
+ bid = (fn_report - 12) / 26;
+ }
+ } else if ((cbits & 0x1e) == 0x02) { /* TCH/H */
+ lch_idx = cbits & 1;
+ if (bi->flags & BI_FLG_SACCH) {
+ uint32_t fn_report;
+ uint8_t tn_report = (tn & ~1) | lch_idx;
+ fn_report = (fn - (tn_report * 13) + 104) % 104;
+ bid = (fn_report - 12) / 26;
+ }
+ } else if ((cbits & 0x1c) == 0x04) { /* SDCCH/4 */
+ lch_idx = cbits & 3;
+ bid = bi->flags & 3;
+ } else if ((cbits & 0x18) == 0x08) { /* SDCCH/8 */
+ lch_idx = cbits & 7;
+ bid = bi->flags & 3;
+ }
+
+ if (bid == -1)
+ return;
+
+ /* Clear if new set */
+ if (bid == 0)
+ memset(bursts, 0x00, 116 * 4);
+
+ /* Unpack (ignore hu/hl) */
+ osmo_pbit2ubit_ext(bt, 0, bi->bits, 0, 57, 0);
+ osmo_pbit2ubit_ext(bt, 59, bi->bits, 57, 57, 0);
+ bt[57] = bt[58] = 1;
+
+ /* A5/x */
+ if (app_state.dch_ciph) {
+ ubit_t ks_dl[114], ks_ul[114], *ks = ul ? ks_ul : ks_dl;
+ osmo_a5(app_state.dch_ciph, app_state.kc, fn, ks_dl, ks_ul);
+ for (i= 0; i< 57; i++) bt[i] ^= ks[i];
+ for (i=59; i<116; i++) bt[i] ^= ks[i-2];
+ }
+
+ /* Convert to softbits */
+ for (i=0; i<116; i++)
+ bursts[(116*bid)+i] = bt[i] ? - (bi->snr >> 1) : (bi->snr >> 1);
+
+ /* If last, decode */
+ if (bid == 3)
+ {
+ uint8_t l2[23];
+ int rv;
+ rv = xcch_decode(l2, bursts);
+
+ if (rv == 0)
+ {
+ uint8_t chan_type, chan_ts, chan_ss;
+ uint8_t gsmtap_chan_type;
+
+ /* Send to GSMTAP */
+ rsl_dec_chan_nr(bi->chan_nr, &chan_type, &chan_ss, &chan_ts);
+ gsmtap_chan_type = chantype_rsl2gsmtap(
+ chan_type,
+ bi->flags & BI_FLG_SACCH ? 0x40 : 0x00
+ );
+ gsmtap_send(gsmtap_inst,
+ arfcn, chan_ts, gsmtap_chan_type, chan_ss,
+ ntohl(bi->frame_nr), bi->rx_level, bi->snr,
+ l2, sizeof(l2)
+ );
+
+ /* Crude CIPH.MOD.COMMAND detect */
+ if ((l2[3] == 0x06) && (l2[4] == 0x35) && (l2[5] & 1))
+ app_state.dch_ciph = 1 + ((l2[5] >> 1) & 7);
+ }
+ }
+}
+
+static char *
+gen_filename(struct osmocom_ms *ms, struct l1ctl_burst_ind *bi)
+{
+ static char buffer[256];
+ time_t d;
+ struct tm lt;
+
+ time(&d);
+ localtime_r(&d, &lt);
+
+ snprintf(buffer, 256, "bursts_%04d%02d%02d_%02d%02d_%d_%d_%02x.dat",
+ lt.tm_year + 1900, lt.tm_mon, lt.tm_mday,
+ lt.tm_hour, lt.tm_min,
+ ms->test_arfcn,
+ ntohl(bi->frame_nr),
+ bi->chan_nr
+ );
+
+ return buffer;
+}
+
+void layer3_rx_burst(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct l1ctl_burst_ind *bi;
+ int16_t rx_dbm;
+ uint16_t arfcn;
+ int ul, do_rel=0;
+
+ /* Header handling */
+ bi = (struct l1ctl_burst_ind *) msg->l1h;
+
+ arfcn = ntohs(bi->band_arfcn);
+ rx_dbm = rxlev2dbm(bi->rx_level);
+ ul = !!(arfcn & ARFCN_UPLINK);
+
+ /* Check for channel start */
+ if (app_state.dch_state == DCH_WAIT_EST) {
+ if (bi->chan_nr == app_state.dch_nr) {
+ if (bi->snr > 64) {
+ /* Change state */
+ app_state.dch_state = DCH_ACTIVE;
+ app_state.dch_badcnt = 0;
+
+ /* Open output */
+ app_state.fh = fopen(gen_filename(ms, bi), "wb");
+ } else {
+ /* Abandon ? */
+ do_rel = (app_state.dch_badcnt++) >= 4;
+ }
+ }
+ }
+
+ /* Check for channel end */
+ if (app_state.dch_state == DCH_ACTIVE) {
+ if (!ul) {
+ /* Bad burst counting */
+ if (bi->snr < 64)
+ app_state.dch_badcnt++;
+ else if (app_state.dch_badcnt >= 2)
+ app_state.dch_badcnt -= 2;
+ else
+ app_state.dch_badcnt = 0;
+
+ /* Release condition */
+ do_rel = app_state.dch_badcnt >= 6;
+ }
+ }
+
+ /* Release ? */
+ if (do_rel) {
+ /* L1 release */
+ l1ctl_tx_dm_rel_req(ms);
+ l1ctl_tx_fbsb_req(ms, ms->test_arfcn,
+ L1CTL_FBSB_F_FB01SB, 100, 0,
+ app_state.ccch_mode);
+
+ /* Change state */
+ app_state.dch_state = DCH_WAIT_REL;
+ app_state.dch_badcnt = 0;
+ app_state.dch_ciph = 0;
+
+ /* Close output */
+ if (app_state.fh) {
+ fclose(app_state.fh);
+ app_state.fh = NULL;
+ }
+ }
+
+ /* Save the burst */
+ if (app_state.dch_state == DCH_ACTIVE)
+ fwrite(bi, sizeof(*bi), 1, app_state.fh);
+
+ /* Try local decoding */
+ if (app_state.dch_state == DCH_ACTIVE)
+ local_burst_decode(bi);
+}
+
+void layer3_app_reset(void)
+{
+ /* Reset state */
+ app_state.has_si1 = 0;
+ app_state.ccch_mode = CCCH_MODE_NONE;
+ app_state.dch_state = DCH_NONE;
+ app_state.dch_badcnt = 0;
+ app_state.dch_ciph = 0;
+
+ if (app_state.fh)
+ fclose(app_state.fh);
+ app_state.fh = NULL;
+
+ memset(&app_state.cell_arfcns, 0x00, sizeof(app_state.cell_arfcns));
+}
+
+static int signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmocom_ms *ms;
+ struct osmobb_msg_ind *mi;
+
+ if (subsys != SS_L1CTL)
+ return 0;
+
+ switch (signal) {
+ case S_L1CTL_BURST_IND:
+ mi = signal_data;
+ layer3_rx_burst(mi->ms, mi->msg);
+ break;
+
+ case S_L1CTL_RESET:
+ ms = signal_data;
+ layer3_app_reset();
+ return l1ctl_tx_fbsb_req(ms, ms->test_arfcn,
+ L1CTL_FBSB_F_FB01SB, 100, 0,
+ CCCH_MODE_NONE);
+ break;
+ }
+ return 0;
+}
+
+
+int l23_app_init(struct osmocom_ms *ms)
+{
+ osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL);
+ l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
+ return layer3_init(ms);
+}
+
+static int l23_cfg_supported()
+{
+ return L23_OPT_TAP | L23_OPT_DBG;
+}
+
+static int l23_getopt_options(struct option **options)
+{
+ static struct option opts [] = {
+ {"kc", 1, 0, 'k'},
+ };
+
+ *options = opts;
+ return ARRAY_SIZE(opts);
+}
+
+static int l23_cfg_print_help()
+{
+ printf("\nApplication specific\n");
+ printf(" -k --kc KEY Key to use to try to decipher DCCHs\n");
+
+ return 0;
+}
+
+static int l23_cfg_handle(int c, const char *optarg)
+{
+ switch (c) {
+ case 'k':
+ if (osmo_hexparse(optarg, app_state.kc, 8) != 8) {
+ fprintf(stderr, "Invalid Kc\n");
+ exit(-1);
+ }
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static struct l23_app_info info = {
+ .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n",
+ .contribution = "Contributions by Holger Hans Peter Freyther\n",
+ .getopt_string = "k:",
+ .cfg_supported = l23_cfg_supported,
+ .cfg_getopt_opt = l23_getopt_options,
+ .cfg_handle_opt = l23_cfg_handle,
+ .cfg_print_help = l23_cfg_print_help,
+};
+
+struct l23_app_info *l23_app_info()
+{
+ return &info;
+}