drop files unrelated to osmo-msc
These either remain from openbsc.git or slipped in while applying recent patches from openbsc.git and do not belong in osmo-msc. Empty out contrib: remove things that are either obviously unrelated to osmo-msc, or seem old and/or esoteric. Change-Id: I49957769e09eed6d723bf7c3777024b62b3480fd
This commit is contained in:
parent
d6d90ce259
commit
47cd0d2687
|
@ -1,170 +0,0 @@
|
||||||
-- Split trace based on SCCP Source
|
|
||||||
-- There are still bugs to find... bugs bugs bugs... hmm
|
|
||||||
do
|
|
||||||
local function init_listener()
|
|
||||||
print("CREATED LISTENER")
|
|
||||||
local tap = Listener.new("ip", "sccp && (ip.src == 172.16.1.81 || ip.dst == 172.16.1.81)")
|
|
||||||
local sccp_type_field = Field.new("sccp.message_type")
|
|
||||||
local sccp_src_field = Field.new("sccp.slr")
|
|
||||||
local sccp_dst_field = Field.new("sccp.dlr")
|
|
||||||
local msg_type_field = Field.new("gsm_a.dtap_msg_mm_type")
|
|
||||||
local lu_rej_field = Field.new("gsm_a.dtap.rej_cause")
|
|
||||||
local ip_src_field = Field.new("ip.src")
|
|
||||||
local ip_dst_field = Field.new("ip.dst")
|
|
||||||
|
|
||||||
--
|
|
||||||
local bssmap_msgtype_field = Field.new("gsm_a.bssmap_msgtype")
|
|
||||||
-- assignment failure 0x03
|
|
||||||
--
|
|
||||||
|
|
||||||
--
|
|
||||||
local dtap_cause_field = Field.new("gsm_a_dtap.cause")
|
|
||||||
local dtap_cc_field = Field.new("gsm_a.dtap_msg_cc_type")
|
|
||||||
|
|
||||||
local connections = {}
|
|
||||||
|
|
||||||
function check_failure(con)
|
|
||||||
check_lu_reject(con)
|
|
||||||
check_disconnect(con)
|
|
||||||
check_failures(con)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- cipher mode reject
|
|
||||||
function check_failures(con)
|
|
||||||
local msgtype = bssmap_msgtype_field()
|
|
||||||
if not msgtype then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
msgtype = tonumber(msgtype)
|
|
||||||
if msgtype == 89 then
|
|
||||||
print("Cipher mode reject")
|
|
||||||
con[4] = true
|
|
||||||
elseif msgtype == 0x03 then
|
|
||||||
print("Assignment failure")
|
|
||||||
con[4] = true
|
|
||||||
elseif msgtype == 0x22 then
|
|
||||||
print("Clear Request... RF failure?")
|
|
||||||
con[4] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check if a DISCONNECT is normal
|
|
||||||
function check_disconnect(con)
|
|
||||||
local msg_type = dtap_cc_field()
|
|
||||||
if not msg_type then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if tonumber(msg_type) ~= 0x25 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local cause = dtap_cause_field()
|
|
||||||
if not cause then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
cause = tonumber(cause)
|
|
||||||
if cause ~= 0x10 then
|
|
||||||
print("DISCONNECT != Normal")
|
|
||||||
con[4] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check if we have a LU Reject
|
|
||||||
function check_lu_reject(con)
|
|
||||||
local msg_type = msg_type_field()
|
|
||||||
if not msg_type then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
msg_type = tonumber(tostring(msg_type))
|
|
||||||
if msg_type == 0x04 then
|
|
||||||
print("LU REJECT with " .. tostring(lu_rej_field()))
|
|
||||||
con[4] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tap.packet(pinfo,tvb,ip)
|
|
||||||
local ip_src = tostring(ip_src_field())
|
|
||||||
local ip_dst = tostring(ip_dst_field())
|
|
||||||
local sccp_type = tonumber(tostring(sccp_type_field()))
|
|
||||||
local sccp_src = sccp_src_field()
|
|
||||||
local sccp_dst = sccp_dst_field()
|
|
||||||
|
|
||||||
local con
|
|
||||||
|
|
||||||
if sccp_type == 0x01 then
|
|
||||||
elseif sccp_type == 0x2 then
|
|
||||||
local src = string.format("%s-%s", ip_src, tostring(sccp_src))
|
|
||||||
local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst))
|
|
||||||
local datestring = os.date("%Y%m%d%H%M%S")
|
|
||||||
local pcap_name = string.format("alink_trace_%s-%s_%s.pcap", src, dst, datestring)
|
|
||||||
local dumper = Dumper.new_for_current(pcap_name)
|
|
||||||
|
|
||||||
local con = { ip_src, tostring(sccp_src), tostring(sccp_dst), false, dumper, pcap_name }
|
|
||||||
|
|
||||||
dumper:dump_current()
|
|
||||||
connections[src] = con
|
|
||||||
connections[dst] = con
|
|
||||||
elseif sccp_type == 0x4 then
|
|
||||||
-- close a connection... remove it from the list
|
|
||||||
local src = string.format("%s-%s", ip_src, tostring(sccp_src))
|
|
||||||
local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst))
|
|
||||||
|
|
||||||
local con = connections[src]
|
|
||||||
if not con then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
con[5]:dump_current()
|
|
||||||
con[5]:flush()
|
|
||||||
|
|
||||||
-- this causes a crash on unpacted wireshark
|
|
||||||
con[5]:close()
|
|
||||||
|
|
||||||
-- the connection had a failure
|
|
||||||
if con[4] == true then
|
|
||||||
local datestring = os.date("%Y%m%d%H%M%S")
|
|
||||||
local new_name = string.format("alink_failure_%s_%s-%s.pcap", datestring, con[2], con[3])
|
|
||||||
os.rename(con[6], new_name)
|
|
||||||
else
|
|
||||||
os.remove(con[6])
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- clear the old connection
|
|
||||||
connections[src] = nil
|
|
||||||
connections[dst] = nil
|
|
||||||
|
|
||||||
elseif sccp_type == 0x5 then
|
|
||||||
-- not handled yet... we should verify stuff here...
|
|
||||||
local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst))
|
|
||||||
local con = connections[dst]
|
|
||||||
if not con then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
con[5]:dump_current()
|
|
||||||
elseif sccp_type == 0x6 then
|
|
||||||
local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst))
|
|
||||||
local con = connections[dst]
|
|
||||||
if not con then
|
|
||||||
print("DON'T KNOW THIS CONNECTION for " .. ip_dst)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
con[5]:dump_current()
|
|
||||||
check_failure(con)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
function tap.draw()
|
|
||||||
print("DRAW")
|
|
||||||
end
|
|
||||||
function tap.reset()
|
|
||||||
print("RESET")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
init_listener()
|
|
||||||
end
|
|
|
@ -1 +0,0 @@
|
||||||
Some crazy scripts call testing... and MSC link failure simulation
|
|
|
@ -1,8 +0,0 @@
|
||||||
ABORT BUSY
|
|
||||||
ABORT 'NO CARRIER'
|
|
||||||
ABORT 'OK'
|
|
||||||
|
|
||||||
'' AT
|
|
||||||
SAY "Dialing a number\n"
|
|
||||||
'OK' ATD05660066;
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Evil dial script..
|
|
||||||
|
|
||||||
while true;
|
|
||||||
do
|
|
||||||
chat -v -f all_dial < /dev/ttyACM0 > /dev/ttyACM0
|
|
||||||
sleep 5s
|
|
||||||
chat -v -f hangup < /dev/ttyACM0 > /dev/ttyACM0
|
|
||||||
sleep 2s
|
|
||||||
done
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
sleep 3
|
|
||||||
echo "enable"
|
|
||||||
sleep 1
|
|
||||||
echo "drop bts connection 0 oml"
|
|
||||||
sleep 1
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
while true;
|
|
||||||
do
|
|
||||||
echo "Going to drop the OML connection"
|
|
||||||
./drop-oml.sh | telnet 127.0.0.1 4242
|
|
||||||
sleep 58m
|
|
||||||
done
|
|
|
@ -1,4 +0,0 @@
|
||||||
TIMEOUT 10
|
|
||||||
'' ^Z
|
|
||||||
SAY "Waiting for hangup confirm\n"
|
|
||||||
'' ATH;
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
while true;
|
|
||||||
do
|
|
||||||
echo "Kill the osmo-bsc"
|
|
||||||
/usr/bin/kill -s SIGUSR2 `pidof osmo-bsc`
|
|
||||||
sleep 58s
|
|
||||||
done
|
|
|
@ -1,120 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
# -*- mode: python-mode; py-indent-tabs-mode: nil -*-
|
|
||||||
"""
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2016 sysmocom s.f.m.c. GmbH
|
|
||||||
*
|
|
||||||
* 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 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, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
"""
|
|
||||||
|
|
||||||
from optparse import OptionParser
|
|
||||||
from ipa import Ctrl
|
|
||||||
import socket
|
|
||||||
|
|
||||||
verbose = False
|
|
||||||
|
|
||||||
def connect(host, port):
|
|
||||||
if verbose:
|
|
||||||
print "Connecting to host %s:%i" % (host, port)
|
|
||||||
|
|
||||||
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
sck.setblocking(1)
|
|
||||||
sck.connect((host, port))
|
|
||||||
return sck
|
|
||||||
|
|
||||||
def do_set_get(sck, var, value = None):
|
|
||||||
(r, c) = Ctrl().cmd(var, value)
|
|
||||||
sck.send(c)
|
|
||||||
answer = Ctrl().rem_header(sck.recv(4096))
|
|
||||||
return (answer,) + Ctrl().verify(answer, r, var, value)
|
|
||||||
|
|
||||||
def set_var(sck, var, val):
|
|
||||||
(a, _, _) = do_set_get(sck, var, val)
|
|
||||||
return a
|
|
||||||
|
|
||||||
def get_var(sck, var):
|
|
||||||
(_, _, v) = do_set_get(sck, var)
|
|
||||||
return v
|
|
||||||
|
|
||||||
def _leftovers(sck, fl):
|
|
||||||
"""
|
|
||||||
Read outstanding data if any according to flags
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
data = sck.recv(1024, fl)
|
|
||||||
except socket.error as (s_errno, strerror):
|
|
||||||
return False
|
|
||||||
if len(data) != 0:
|
|
||||||
tail = data
|
|
||||||
while True:
|
|
||||||
(head, tail) = Ctrl().split_combined(tail)
|
|
||||||
print "Got message:", Ctrl().rem_header(head)
|
|
||||||
if len(tail) == 0:
|
|
||||||
break
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = OptionParser("Usage: %prog [options] var [value]")
|
|
||||||
parser.add_option("-d", "--host", dest="host",
|
|
||||||
help="connect to HOST", metavar="HOST")
|
|
||||||
parser.add_option("-p", "--port", dest="port", type="int",
|
|
||||||
help="use PORT", metavar="PORT", default=4249)
|
|
||||||
parser.add_option("-g", "--get", action="store_true",
|
|
||||||
dest="cmd_get", help="perform GET operation")
|
|
||||||
parser.add_option("-s", "--set", action="store_true",
|
|
||||||
dest="cmd_set", help="perform SET operation")
|
|
||||||
parser.add_option("-v", "--verbose", action="store_true",
|
|
||||||
dest="verbose", help="be verbose", default=False)
|
|
||||||
parser.add_option("-m", "--monitor", action="store_true",
|
|
||||||
dest="monitor", help="monitor the connection for traps", default=False)
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
verbose = options.verbose
|
|
||||||
|
|
||||||
if options.cmd_set and options.cmd_get:
|
|
||||||
parser.error("Get and set options are mutually exclusive!")
|
|
||||||
|
|
||||||
if not (options.cmd_get or options.cmd_set or options.monitor):
|
|
||||||
parser.error("One of -m, -g, or -s must be set")
|
|
||||||
|
|
||||||
if not (options.host):
|
|
||||||
parser.error("Destination host and port required!")
|
|
||||||
|
|
||||||
sock = connect(options.host, options.port)
|
|
||||||
|
|
||||||
if options.cmd_set:
|
|
||||||
if len(args) < 2:
|
|
||||||
parser.error("Set requires var and value arguments")
|
|
||||||
_leftovers(sock, socket.MSG_DONTWAIT)
|
|
||||||
print "Got message:", set_var(sock, args[0], ' '.join(args[1:]))
|
|
||||||
|
|
||||||
if options.cmd_get:
|
|
||||||
if len(args) != 1:
|
|
||||||
parser.error("Get requires the var argument")
|
|
||||||
_leftovers(sock, socket.MSG_DONTWAIT)
|
|
||||||
(a, _, _) = do_set_get(sock, args[0])
|
|
||||||
print "Got message:", a
|
|
||||||
|
|
||||||
if options.monitor:
|
|
||||||
while True:
|
|
||||||
if not _leftovers(sock, 0):
|
|
||||||
print "Connection is gone."
|
|
||||||
break
|
|
||||||
sock.close()
|
|
|
@ -1,33 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
f = open("unbalanced")
|
|
||||||
lines = []
|
|
||||||
for line in f:
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
filenames = {}
|
|
||||||
|
|
||||||
output = []
|
|
||||||
for line in lines:
|
|
||||||
if "[0x" in line:
|
|
||||||
start = line.find("[")
|
|
||||||
end = line.find("]")
|
|
||||||
addr = line[start+1:end]
|
|
||||||
try:
|
|
||||||
file = filenames[addr]
|
|
||||||
except KeyError:
|
|
||||||
r = os.popen("addr2line -fs -e ./bsc_hack %s" % addr)
|
|
||||||
all = r.read().replace("\n", ",")
|
|
||||||
file = all
|
|
||||||
filenames[addr] = file
|
|
||||||
|
|
||||||
line = line.replace(addr, file)
|
|
||||||
output.append(line)
|
|
||||||
|
|
||||||
g = open("unbalanced.2", "w")
|
|
||||||
g.write("".join(output))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
#
|
|
||||||
# Convert ETSI documents to an enum
|
|
||||||
#
|
|
||||||
|
|
||||||
import re, sys
|
|
||||||
|
|
||||||
def convert(string):
|
|
||||||
string = string.strip().replace(" ", "").rjust(8, "0")
|
|
||||||
var = 0
|
|
||||||
offset = 7
|
|
||||||
for char in string:
|
|
||||||
assert offset >= 0
|
|
||||||
var = var | (int(char) << offset)
|
|
||||||
offset = offset - 1
|
|
||||||
|
|
||||||
return var
|
|
||||||
|
|
||||||
def string(name):
|
|
||||||
name = name.replace(" ", "_")
|
|
||||||
name = name.replace('"', "")
|
|
||||||
name = name.replace('/', '_')
|
|
||||||
name = name.replace('(', '_')
|
|
||||||
name = name.replace(')', '_')
|
|
||||||
return "%s_%s" % (sys.argv[2], name.upper())
|
|
||||||
|
|
||||||
file = open(sys.argv[1])
|
|
||||||
|
|
||||||
|
|
||||||
for line in file:
|
|
||||||
m = re.match(r"[ \t]*(?P<value>[01 ]+)[ ]+(?P<name>[a-zA-Z /0-9()]+)", line[:-1])
|
|
||||||
|
|
||||||
if m:
|
|
||||||
print "\t%s\t\t= %d," % (string(m.groupdict()["name"]), convert(m.groupdict()["value"]))
|
|
||||||
else:
|
|
||||||
print line[:-1]
|
|
|
@ -1,147 +0,0 @@
|
||||||
#!/usr/bin/python2
|
|
||||||
|
|
||||||
mod_license = '''
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2016 sysmocom s.f.m.c. GmbH
|
|
||||||
*
|
|
||||||
* 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 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, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
'''
|
|
||||||
|
|
||||||
import sys, argparse, random, logging, tornado.ioloop, tornado.web, tornado.tcpclient, tornado.httpclient, eventsource, bsc_control
|
|
||||||
from eventsource import listener, request
|
|
||||||
|
|
||||||
'''
|
|
||||||
N. B: this is not an example of building proper REST API or building secure web application.
|
|
||||||
It's only purpose is to illustrate conversion of Osmocom's Control Interface to web-friendly API.
|
|
||||||
Exposing this to Internet while connected to production network might lead to all sorts of mischief and mayhem
|
|
||||||
from NSA' TAO breaking into your network to zombie apocalypse. Do NOT do that.
|
|
||||||
'''
|
|
||||||
|
|
||||||
token = None
|
|
||||||
stream = None
|
|
||||||
url = None
|
|
||||||
|
|
||||||
'''
|
|
||||||
Returns json according to following schema - see http://json-schema.org/documentation.html for details:
|
|
||||||
{
|
|
||||||
"title": "Ctrl Schema",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"variable": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"varlue": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["interface", "variable", "value"]
|
|
||||||
}
|
|
||||||
Example validation from command-line:
|
|
||||||
json validate --schema-file=schema.json --document-file=data.json
|
|
||||||
The interface is represented as string because it might look different for IPv4 vs v6.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def read_header(data):
|
|
||||||
t_length = bsc_control.ipa_ctrl_header(data)
|
|
||||||
if (t_length):
|
|
||||||
stream.read_bytes(t_length - 1, callback = read_trap)
|
|
||||||
else:
|
|
||||||
print >> sys.stderr, "protocol error: length missing in %s!" % data
|
|
||||||
|
|
||||||
@tornado.gen.coroutine
|
|
||||||
def read_trap(data):
|
|
||||||
(t, z, v, p) = data.split()
|
|
||||||
if (t != 'TRAP' or int(z) != 0):
|
|
||||||
print >> sys.stderr, "protocol error: TRAP != %s or 0! = %d" % (t, int(z))
|
|
||||||
else:
|
|
||||||
yield tornado.httpclient.AsyncHTTPClient().fetch(tornado.httpclient.HTTPRequest(url = "%s/%s/%s" % (url, "ping", token),
|
|
||||||
method = 'POST',
|
|
||||||
headers = {'Content-Type': 'application/json'},
|
|
||||||
body = tornado.escape.json_encode({ 'variable' : v, 'value' : p })))
|
|
||||||
stream.read_bytes(4, callback = read_header)
|
|
||||||
|
|
||||||
@tornado.gen.coroutine
|
|
||||||
def trap_setup(host, port, target_host, target_port, tk):
|
|
||||||
global stream
|
|
||||||
global url
|
|
||||||
global token
|
|
||||||
token = tk
|
|
||||||
url = "http://%s:%s/sse" % (host, port)
|
|
||||||
stream = yield tornado.tcpclient.TCPClient().connect(target_host, target_port)
|
|
||||||
stream.read_bytes(4, callback = read_header)
|
|
||||||
|
|
||||||
def get_v(s, v):
|
|
||||||
return { 'variable' : v, 'value' : bsc_control.get_var(s, tornado.escape.native_str(v)) }
|
|
||||||
|
|
||||||
class CtrlHandler(tornado.web.RequestHandler):
|
|
||||||
def initialize(self):
|
|
||||||
self.skt = bsc_control.connect(self.settings['ctrl_host'], self.settings['ctrl_port'])
|
|
||||||
|
|
||||||
def get(self, v):
|
|
||||||
self.write(get_v(self.skt, v))
|
|
||||||
|
|
||||||
def post(self):
|
|
||||||
self.write(get_v(self.skt, self.get_argument("variable")))
|
|
||||||
|
|
||||||
class SetCtrl(CtrlHandler):
|
|
||||||
def get(self, var, val):
|
|
||||||
bsc_control.set_var(self.skt, tornado.escape.native_str(var), tornado.escape.native_str(val))
|
|
||||||
super(SetCtrl, self).get(tornado.escape.native_str(var))
|
|
||||||
|
|
||||||
def post(self):
|
|
||||||
bsc_control.set_var(self.skt, tornado.escape.native_str(self.get_argument("variable")), tornado.escape.native_str(self.get_argument("value")))
|
|
||||||
super(SetCtrl, self).post()
|
|
||||||
|
|
||||||
class Slash(tornado.web.RequestHandler):
|
|
||||||
def get(self):
|
|
||||||
self.write('<html><head><title>%s</title></head><body>Using Tornado framework v%s'
|
|
||||||
'<form action="/get" method="POST">'
|
|
||||||
'<input type="text" name="variable">'
|
|
||||||
'<input type="submit" value="GET">'
|
|
||||||
'</form>'
|
|
||||||
'<form action="/set" method="POST">'
|
|
||||||
'<input type="text" name="variable">'
|
|
||||||
'<input type="text" name="value">'
|
|
||||||
'<input type="submit" value="SET">'
|
|
||||||
'</form>'
|
|
||||||
'</body></html>' % ("Osmocom Control Interface Proxy", tornado.version))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
p = argparse.ArgumentParser(description='Osmocom Control Interface proxy.')
|
|
||||||
p.add_argument('-c', '--control-port', type = int, default = 4252, help = "Target Control Interface port")
|
|
||||||
p.add_argument('-a', '--control-host', default = 'localhost', help = "Target Control Interface adress")
|
|
||||||
p.add_argument('-b', '--host', default = 'localhost', help = "Adress to bind proxy's web interface")
|
|
||||||
p.add_argument('-p', '--port', type = int, default = 6969, help = "Port to bind proxy's web interface")
|
|
||||||
p.add_argument('-d', '--debug', action='store_true', help = "Activate debugging (default off)")
|
|
||||||
p.add_argument('-t', '--token', default = 'osmocom', help = "Token to be used by SSE client in URL e. g. http://127.0.0.1:8888/poll/osmocom where 'osmocom' is default token value")
|
|
||||||
p.add_argument('-k', '--keepalive', type = int, default = 5000, help = "Timeout betwwen keepalive messages, in milliseconds, defaults to 5000")
|
|
||||||
args = p.parse_args()
|
|
||||||
random.seed()
|
|
||||||
tornado.netutil.Resolver.configure('tornado.netutil.ThreadedResolver') # Use non-blocking resolver
|
|
||||||
logging.basicConfig()
|
|
||||||
application = tornado.web.Application([
|
|
||||||
(r"/", Slash),
|
|
||||||
(r"/get", CtrlHandler),
|
|
||||||
(r"/get/(.*)", CtrlHandler),
|
|
||||||
(r"/set", SetCtrl),
|
|
||||||
(r"/set/(.*)/(.*)", SetCtrl),
|
|
||||||
(r"/sse/(.*)/(.*)", listener.EventSourceHandler, dict(event_class = listener.JSONIdEvent, keepalive = args.keepalive)),
|
|
||||||
], debug = args.debug, ctrl_host = args.control_host, ctrl_port = args.control_port)
|
|
||||||
application.listen(address = args.host, port = args.port)
|
|
||||||
trap_setup(args.host, args.port, application.settings['ctrl_host'], application.settings['ctrl_port'], args.token)
|
|
||||||
tornado.ioloop.IOLoop.instance().start()
|
|
|
@ -1,58 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
demonstrate a unblock bug on the GB Proxy..
|
|
||||||
"""
|
|
||||||
|
|
||||||
bts_ns_reset = "\x02\x00\x81\x01\x01\x82\x1f\xe7\x04\x82\x1f\xe7"
|
|
||||||
ns_reset_ack = "\x03\x01\x82\x1f\xe7\x04\x82\x1f\xe7"
|
|
||||||
|
|
||||||
bts_ns_unblock = "\x06"
|
|
||||||
ns_unblock_ack = "\x07"
|
|
||||||
|
|
||||||
bts_bvc_reset_0 = "\x00\x00\x00\x00\x22\x04\x82\x00\x00\x07\x81\x03\x3b\x81\x02"
|
|
||||||
ns_bvc_reset_0_ack = "\x00\x00\x00\x00\x23\x04\x82\x00\x00"
|
|
||||||
|
|
||||||
bts_bvc_reset_8167 = "\x00\x00\x00\x00\x22\x04\x82\x1f\xe7\x07\x81\x08\x08\x88\x72\xf4\x80\x10\x1c\x00\x9c\x40"
|
|
||||||
|
|
||||||
|
|
||||||
import socket
|
|
||||||
socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
socket.bind(("0.0.0.0", 0))
|
|
||||||
socket.setblocking(1)
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
port = int(sys.argv[1])
|
|
||||||
print "Sending data to port: %d" % port
|
|
||||||
|
|
||||||
def send_and_receive(packet):
|
|
||||||
socket.sendto(packet, ("127.0.0.1", port))
|
|
||||||
|
|
||||||
try:
|
|
||||||
data, addr = socket.recvfrom(4096)
|
|
||||||
except socket.error, e:
|
|
||||||
print "ERROR", e
|
|
||||||
import sys
|
|
||||||
sys.exit(0)
|
|
||||||
return data
|
|
||||||
|
|
||||||
#send stuff once
|
|
||||||
|
|
||||||
to_send = [
|
|
||||||
(bts_ns_reset, ns_reset_ack, "reset ack"),
|
|
||||||
(bts_ns_unblock, ns_unblock_ack, "unblock ack"),
|
|
||||||
(bts_bvc_reset_0, ns_bvc_reset_0_ack, "BVCI=0 reset ack"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
for (out, inp, type) in to_send:
|
|
||||||
res = send_and_receive(out)
|
|
||||||
if res != inp:
|
|
||||||
print "Failed to get the %s" % type
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
import time
|
|
||||||
time.sleep(3)
|
|
||||||
res = send_and_receive(bts_bvc_reset_8167)
|
|
||||||
print "Sent all messages... check wireshark for the last response"
|
|
|
@ -1,78 +0,0 @@
|
||||||
-- Simple LUA script to print the size of BSSGP messages over their type...
|
|
||||||
|
|
||||||
do
|
|
||||||
local ip_bucket = {}
|
|
||||||
|
|
||||||
local pdu_types = {}
|
|
||||||
pdu_types[ 6] = "PAGING"
|
|
||||||
pdu_types[11] = "SUSPEND"
|
|
||||||
pdu_types[12] = "SUSPEND-ACK"
|
|
||||||
pdu_types[32] = "BVC-BLOCK"
|
|
||||||
pdu_types[33] = "BVC-BLOCK-ACK"
|
|
||||||
pdu_types[34] = "BVC-RESET"
|
|
||||||
pdu_types[35] = "BVC-RESET-ACK"
|
|
||||||
pdu_types[36] = "UNBLOCK"
|
|
||||||
pdu_types[37] = "UNBLOCK-ACK"
|
|
||||||
pdu_types[38] = "FLOW-CONTROL-BVC"
|
|
||||||
pdu_types[39] = "FLOW-CONTROL-BVC-ACK"
|
|
||||||
pdu_types[40] = "FLOW-CONTROL-MS"
|
|
||||||
pdu_types[41] = "FLOW-CONTROL-MS-ACK"
|
|
||||||
pdu_types[44] = "LLC-DISCARDED"
|
|
||||||
|
|
||||||
local function init_listener()
|
|
||||||
-- handle the port as NS over IP
|
|
||||||
local udp_port_table = DissectorTable.get("udp.port")
|
|
||||||
local gprs_ns_dis = Dissector.get("gprs_ns")
|
|
||||||
udp_port_table:add(23000,gprs_ns_dis)
|
|
||||||
|
|
||||||
-- bssgp filters
|
|
||||||
local bssgp_pdu_get = Field.new("bssgp.pdu_type")
|
|
||||||
local udp_length_get = Field.new("udp.length")
|
|
||||||
|
|
||||||
local tap = Listener.new("ip", "udp.port == 23000")
|
|
||||||
function tap.packet(pinfo,tvb,ip)
|
|
||||||
local pdu = bssgp_pdu_get()
|
|
||||||
local len = udp_length_get()
|
|
||||||
|
|
||||||
-- only handle bssgp, but we also want the IP frame
|
|
||||||
if not pdu then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
pdu = tostring(pdu)
|
|
||||||
if tonumber(pdu) == 0 or tonumber(pdu) == 1 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local ip_src = tostring(ip.ip_src)
|
|
||||||
local bssgp_histo = ip_bucket[ip_src]
|
|
||||||
if not bssgp_histo then
|
|
||||||
bssgp_histo = {}
|
|
||||||
ip_bucket[ip_src] = bssgp_histo
|
|
||||||
end
|
|
||||||
|
|
||||||
local key = pdu
|
|
||||||
local bucket = bssgp_histo[key]
|
|
||||||
if not bucket then
|
|
||||||
bucket = {}
|
|
||||||
bssgp_histo[key] = bucket
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(bucket, tostring(len))
|
|
||||||
print("IP: " .. ip_src .. " PDU: " .. pdu_types[tonumber(pdu)] .. " Length: " .. tostring(len))
|
|
||||||
end
|
|
||||||
|
|
||||||
function tap.draw()
|
|
||||||
-- well... this will not be called...
|
|
||||||
-- for ip,bssgp_histo in pairs(dumpers) do
|
|
||||||
-- print("IP " .. ip)
|
|
||||||
-- end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tap.reset()
|
|
||||||
-- well... this will not be called...
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
init_listener()
|
|
||||||
end
|
|
|
@ -1,80 +0,0 @@
|
||||||
-- I count the buffer space needed for LLC PDUs in the worse case and print it
|
|
||||||
|
|
||||||
do
|
|
||||||
local function init_listener()
|
|
||||||
-- handle the port as NS over IP
|
|
||||||
local udp_port_table = DissectorTable.get("udp.port")
|
|
||||||
local gprs_ns_dis = Dissector.get("gprs_ns")
|
|
||||||
udp_port_table:add(23000,gprs_ns_dis)
|
|
||||||
|
|
||||||
-- bssgp filters
|
|
||||||
local bssgp_pdu_get = Field.new("bssgp.pdu_type")
|
|
||||||
local bssgp_delay_get = Field.new("bssgp.delay_val")
|
|
||||||
local llcgprs_get = Field.new("llcgprs")
|
|
||||||
local pdus = nil
|
|
||||||
|
|
||||||
print("START...")
|
|
||||||
|
|
||||||
local tap = Listener.new("ip", "udp.port == 23000 && bssgp.pdu_type == 0")
|
|
||||||
function tap.packet(pinfo,tvb,ip)
|
|
||||||
local pdu = bssgp_pdu_get()
|
|
||||||
local len = llcgprs_get().len
|
|
||||||
local delay = bssgp_delay_get()
|
|
||||||
|
|
||||||
-- only handle bssgp, but we also want the IP frame
|
|
||||||
if not pdu then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if tonumber(tostring(delay)) == 65535 then
|
|
||||||
pdus = { next = pdus,
|
|
||||||
len = len,
|
|
||||||
expires = -1 }
|
|
||||||
else
|
|
||||||
local off = tonumber(tostring(delay)) / 100.0
|
|
||||||
pdus = { next = pdus,
|
|
||||||
len = len,
|
|
||||||
expires = pinfo.rel_ts + off }
|
|
||||||
end
|
|
||||||
local now_time = tonumber(tostring(pinfo.rel_ts))
|
|
||||||
local now_size = 0
|
|
||||||
local l = pdus
|
|
||||||
local prev = nil
|
|
||||||
local count = 0
|
|
||||||
while l do
|
|
||||||
if now_time < l.expires or l.expires == -1 then
|
|
||||||
now_size = now_size + l.len
|
|
||||||
prev = l
|
|
||||||
l = l.next
|
|
||||||
count = count + 1
|
|
||||||
else
|
|
||||||
-- delete things
|
|
||||||
if prev == nil then
|
|
||||||
pdus = nil
|
|
||||||
l = nil
|
|
||||||
else
|
|
||||||
prev.next = l.next
|
|
||||||
l = l.next
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- print("TOTAL: " .. now_time .. " PDU_SIZE: " .. now_size)
|
|
||||||
print(now_time .. " " .. now_size / 1024.0 .. " " .. count)
|
|
||||||
-- print("NOW: " .. tostring(pinfo.rel_ts) .. " Delay: " .. tostring(delay) .. " Length: " .. tostring(len))
|
|
||||||
end
|
|
||||||
|
|
||||||
function tap.draw()
|
|
||||||
-- well... this will not be called...
|
|
||||||
-- for ip,bssgp_histo in pairs(dumpers) do
|
|
||||||
-- print("IP " .. ip)
|
|
||||||
-- end
|
|
||||||
print("END")
|
|
||||||
end
|
|
||||||
|
|
||||||
function tap.reset()
|
|
||||||
-- well... this will not be called...
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
init_listener()
|
|
||||||
end
|
|
|
@ -1,46 +0,0 @@
|
||||||
-- Create a file named by_ip/''ip_addess''.cap with all ip traffic of each ip host. (works for tshark only)
|
|
||||||
-- Dump files are created for both source and destination hosts
|
|
||||||
do
|
|
||||||
local dir = "by_tlli"
|
|
||||||
local dumpers = {}
|
|
||||||
local function init_listener()
|
|
||||||
local udp_port_table = DissectorTable.get("udp.port")
|
|
||||||
local gprs_ns_dis = Dissector.get("gprs_ns")
|
|
||||||
udp_port_table:add(23000,gprs_ns_dis)
|
|
||||||
|
|
||||||
local field_tlli = Field.new("bssgp.tlli")
|
|
||||||
local tap = Listener.new("ip", "udp.port == 23000")
|
|
||||||
|
|
||||||
-- we will be called once for every IP Header.
|
|
||||||
-- If there's more than one IP header in a given packet we'll dump the packet once per every header
|
|
||||||
function tap.packet(pinfo,tvb,ip)
|
|
||||||
local tlli = field_tlli()
|
|
||||||
if not tlli then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local tlli_str = tostring(tlli)
|
|
||||||
tlli_dmp = dumpers[tlli_str]
|
|
||||||
if not tlli_dmp then
|
|
||||||
local tlli_hex = string.format("0x%x", tonumber(tlli_str))
|
|
||||||
print("Creating dump for TLLI " .. tlli_hex)
|
|
||||||
tlli_dmp = Dumper.new_for_current(dir .. "/" .. tlli_hex .. ".pcap")
|
|
||||||
dumpers[tlli_str] = tlli_dmp
|
|
||||||
end
|
|
||||||
tlli_dmp:dump_current()
|
|
||||||
tlli_dmp:flush()
|
|
||||||
end
|
|
||||||
function tap.draw()
|
|
||||||
for tlli,dumper in pairs(dumpers) do
|
|
||||||
dumper:flush()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function tap.reset()
|
|
||||||
for tlli,dumper in pairs(dumpers) do
|
|
||||||
dumper:close()
|
|
||||||
end
|
|
||||||
dumpers = {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
init_listener()
|
|
||||||
end
|
|
|
@ -1,59 +0,0 @@
|
||||||
-- This script verifies that the N(U) is increasing...
|
|
||||||
--
|
|
||||||
do
|
|
||||||
local nu_state_src = {}
|
|
||||||
|
|
||||||
local function init_listener()
|
|
||||||
-- handle the port as NS over IP
|
|
||||||
local udp_port_table = DissectorTable.get("udp.port")
|
|
||||||
local gprs_ns_dis = Dissector.get("gprs_ns")
|
|
||||||
udp_port_table:add(23000,gprs_ns_dis)
|
|
||||||
|
|
||||||
-- we want to look here...
|
|
||||||
local llc_sapi_get = Field.new("llcgprs.sapib")
|
|
||||||
local llc_nu_get = Field.new("llcgprs.nu")
|
|
||||||
local bssgp_tlli_get = Field.new("bssgp.tlli")
|
|
||||||
|
|
||||||
local tap = Listener.new("ip", "udp.port == 23000")
|
|
||||||
function tap.packet(pinfo,tvb,ip)
|
|
||||||
local llc_sapi = llc_sapi_get()
|
|
||||||
local llc_nu = llc_nu_get()
|
|
||||||
local bssgp_tlli = bssgp_tlli_get()
|
|
||||||
|
|
||||||
if not llc_sapi or not llc_nu or not bssgp_tlli then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local ip_src = tostring(ip.ip_src)
|
|
||||||
local bssgp_tlli = tostring(bssgp_tlli)
|
|
||||||
local llc_nu = tostring(llc_nu)
|
|
||||||
local llc_sapi = tostring(llc_sapi)
|
|
||||||
|
|
||||||
local src_key = ip_src .. "-" .. bssgp_tlli .. "-" .. llc_sapi
|
|
||||||
local last_nu = nu_state_src[src_key]
|
|
||||||
if not last_nu then
|
|
||||||
-- print("Establishing mapping for " .. src_key)
|
|
||||||
nu_state_src[src_key] = llc_nu
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local function tohex(number)
|
|
||||||
return string.format("0x%x", tonumber(number))
|
|
||||||
end
|
|
||||||
|
|
||||||
nu_state_src[src_key] = llc_nu
|
|
||||||
if tonumber(last_nu) + 1 ~= tonumber(llc_nu) then
|
|
||||||
print("JUMP in N(U) on TLLI " .. tohex(bssgp_tlli) .. " and SAPI: " .. llc_sapi .. " src: " .. ip_src)
|
|
||||||
print("\t last: " .. last_nu .. " now: " .. llc_nu)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tap.draw()
|
|
||||||
end
|
|
||||||
|
|
||||||
function tap.reset()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
init_listener()
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
-- Remove old data from the database
|
|
||||||
DELETE FROM Subscriber
|
|
||||||
WHERE id != 1 AND datetime('now', '-10 days') > updated AND authorized != 1;
|
|
||||||
DELETE FROM Equipment
|
|
||||||
WHERE datetime('now', '-10 days') > updated;
|
|
||||||
DELETE FROM EquipmentWatch
|
|
||||||
WHERE datetime('now', '-10 days') > updated;
|
|
||||||
DELETE FROM SMS
|
|
||||||
WHERE datetime('now', '-10 days') > created;
|
|
||||||
DELETE FROM VLR
|
|
||||||
WHERE datetime('now', '-10 days') > updated;
|
|
||||||
DELETE FROM ApduBlobs
|
|
||||||
WHERE datetime('now', '-10 days') > created;
|
|
||||||
DELETE FROM Counters
|
|
||||||
WHERE datetime('now', '-10 days') > timestamp;
|
|
||||||
DELETE FROM RateCounters
|
|
||||||
WHERE datetime('now', '-10 days') > timestamp;
|
|
||||||
VACUUM;
|
|
|
@ -1,125 +0,0 @@
|
||||||
#!/usr/bin/python2.5
|
|
||||||
|
|
||||||
from __future__ import with_statement
|
|
||||||
|
|
||||||
from pysqlite2 import dbapi2 as sqlite3
|
|
||||||
import sys
|
|
||||||
|
|
||||||
hlr = sqlite3.connect(sys.argv[1])
|
|
||||||
web = sqlite3.connect(sys.argv[2])
|
|
||||||
|
|
||||||
# switch to autocommit
|
|
||||||
hlr.isolation_level = None
|
|
||||||
web.isolation_level = None
|
|
||||||
|
|
||||||
hlr.row_factory = sqlite3.Row
|
|
||||||
web.row_factory = sqlite3.Row
|
|
||||||
|
|
||||||
with hlr:
|
|
||||||
hlr_subscrs = hlr.execute("""
|
|
||||||
SELECT * FROM Subscriber
|
|
||||||
""").fetchall()
|
|
||||||
hlr_tokens = hlr.execute("""
|
|
||||||
SELECT * FROM AuthToken
|
|
||||||
""").fetchall()
|
|
||||||
|
|
||||||
with web:
|
|
||||||
web_tokens = web.execute("""
|
|
||||||
SELECT * FROM reg_tokens
|
|
||||||
""").fetchall()
|
|
||||||
web_sms = web.execute("""
|
|
||||||
SELECT * FROM sms_queue
|
|
||||||
""").fetchall()
|
|
||||||
|
|
||||||
# index by subscr id
|
|
||||||
hlr_subscrs_by_id = {}
|
|
||||||
hlr_subscrs_by_ext = {}
|
|
||||||
hlr_tokens_by_subscr_id = {}
|
|
||||||
for x in hlr_subscrs:
|
|
||||||
hlr_subscrs_by_id[x['id']] = x
|
|
||||||
hlr_subscrs_by_ext[x['extension']] = x
|
|
||||||
del hlr_subscrs
|
|
||||||
for x in hlr_tokens:
|
|
||||||
hlr_tokens_by_subscr_id[x['subscriber_id']] = x
|
|
||||||
del hlr_tokens
|
|
||||||
|
|
||||||
web_tokens_by_subscr_id = {}
|
|
||||||
for x in web_tokens:
|
|
||||||
web_tokens_by_subscr_id[x['subscriber_id']] = x
|
|
||||||
del web_tokens
|
|
||||||
|
|
||||||
# remove leftover web_tokens and correct inconsistent fields
|
|
||||||
with web:
|
|
||||||
for x in web_tokens_by_subscr_id.values():
|
|
||||||
subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None)
|
|
||||||
if subscr is None:
|
|
||||||
web.execute("""
|
|
||||||
DELETE FROM reg_tokens WHERE subscriber_id = ?
|
|
||||||
""", (x['subscriber_id'],))
|
|
||||||
del web_tokens_by_subscr_id[x['subscriber_id']]
|
|
||||||
continue
|
|
||||||
if str(x['imsi']) != str(subscr['imsi']) or \
|
|
||||||
x['extension'] != subscr['extension'] or \
|
|
||||||
x['tmsi'] != subscr['tmsi'] or \
|
|
||||||
x['lac'] != subscr['lac']:
|
|
||||||
web.execute("""
|
|
||||||
UPDATE reg_tokens
|
|
||||||
SET imsi = ?, extension = ?, tmsi = ?, lac = ?
|
|
||||||
WHERE subscriber_id = ?
|
|
||||||
""", (str(subscr['imsi']), subscr['extension'],
|
|
||||||
subscr['tmsi'], subscr['lac'], x['subscriber_id']))
|
|
||||||
|
|
||||||
# add missing web_tokens
|
|
||||||
with web:
|
|
||||||
for x in hlr_tokens_by_subscr_id.values():
|
|
||||||
subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None)
|
|
||||||
if subscr is None:
|
|
||||||
hlr.execute("""
|
|
||||||
DELETE FROM AuthToken WHERE subscriber_id = ?
|
|
||||||
""", (x['subscriber_id'],))
|
|
||||||
del hlr_tokens_by_subscr_id[x['subscriber_id']]
|
|
||||||
continue
|
|
||||||
webtoken = web_tokens_by_subscr_id.get(x['subscriber_id'], None)
|
|
||||||
if webtoken is None:
|
|
||||||
web.execute("""
|
|
||||||
INSERT INTO reg_tokens
|
|
||||||
(subscriber_id, extension, reg_completed, name, email, lac, imsi, token, tmsi)
|
|
||||||
VALUES
|
|
||||||
(?, ?, 0, ?, '', ?, ?, ?, ?)
|
|
||||||
""", (x['subscriber_id'], subscr['extension'], subscr['name'],
|
|
||||||
subscr['lac'], str(subscr['imsi']), x['token'], subscr['tmsi']))
|
|
||||||
|
|
||||||
# authorize subscribers
|
|
||||||
with hlr:
|
|
||||||
for x in web_tokens_by_subscr_id.values():
|
|
||||||
subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None)
|
|
||||||
if x['reg_completed'] and not subscr['authorized']:
|
|
||||||
hlr.execute("""
|
|
||||||
UPDATE Subscriber
|
|
||||||
SET authorized = 1
|
|
||||||
WHERE id = ?
|
|
||||||
""", (x['subscriber_id'],))
|
|
||||||
|
|
||||||
# Sync SMS from web to hlr
|
|
||||||
with hlr:
|
|
||||||
for sms in web_sms:
|
|
||||||
subscr = hlr_subscrs_by_ext.get(sms['receiver_ext'])
|
|
||||||
if subscr is None:
|
|
||||||
print '%s not found' % sms['receiver_ext']
|
|
||||||
continue
|
|
||||||
hlr.execute("""
|
|
||||||
INSERT INTO SMS
|
|
||||||
(created, sender_id, receiver_id, reply_path_req, status_rep_req, protocol_id, data_coding_scheme, ud_hdr_ind, text)
|
|
||||||
VALUES
|
|
||||||
(?, 1, ?, 0, 0, 0, 0, 0, ?)
|
|
||||||
""", (sms['created'], subscr['id'], sms['text']))
|
|
||||||
with web:
|
|
||||||
for sms in web_sms:
|
|
||||||
web.execute("""
|
|
||||||
DELETE FROM sms_queue WHERE id = ?
|
|
||||||
""", (sms['id'],))
|
|
||||||
|
|
||||||
|
|
||||||
hlr.close()
|
|
||||||
web.close()
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Simple server for mgcp... send audit, receive response..
|
|
||||||
|
|
||||||
import socket, time
|
|
||||||
|
|
||||||
MGCP_GATEWAY_PORT = 2427
|
|
||||||
MGCP_CALLAGENT_PORT = 2727
|
|
||||||
|
|
||||||
rsip_resp = """200 321321332\r\n"""
|
|
||||||
audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n"""
|
|
||||||
crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n"""
|
|
||||||
dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n"""
|
|
||||||
mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 6666 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
|
|
||||||
|
|
||||||
def hexdump(src, length=8):
|
|
||||||
"""Recipe is from http://code.activestate.com/recipes/142812/"""
|
|
||||||
result = []
|
|
||||||
digits = 4 if isinstance(src, unicode) else 2
|
|
||||||
for i in xrange(0, len(src), length):
|
|
||||||
s = src[i:i+length]
|
|
||||||
hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s])
|
|
||||||
text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
|
|
||||||
result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) )
|
|
||||||
return b'\n'.join(result)
|
|
||||||
|
|
||||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT))
|
|
||||||
server_socket.setblocking(1)
|
|
||||||
|
|
||||||
last_ci = 1
|
|
||||||
def send_and_receive(packet):
|
|
||||||
global last_ci
|
|
||||||
server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT))
|
|
||||||
try:
|
|
||||||
data, addr = server_socket.recvfrom(4096)
|
|
||||||
|
|
||||||
# attempt to store the CI of the response
|
|
||||||
list = data.split("\n")
|
|
||||||
for item in list:
|
|
||||||
if item.startswith("I: "):
|
|
||||||
last_ci = int(item[3:])
|
|
||||||
|
|
||||||
print hexdump(data), addr
|
|
||||||
except socket.error, e:
|
|
||||||
print e
|
|
||||||
pass
|
|
||||||
|
|
||||||
def generate_tid():
|
|
||||||
import random
|
|
||||||
return random.randint(0, 65123)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
while True:
|
|
||||||
send_and_receive(audit_packet % generate_tid())
|
|
||||||
send_and_receive(crcx_packet % generate_tid() )
|
|
||||||
send_and_receive(mdcx_packet % (generate_tid(), last_ci))
|
|
||||||
send_and_receive(dlcx_packet % (generate_tid(), last_ci))
|
|
||||||
|
|
||||||
time.sleep(3)
|
|
|
@ -1,30 +0,0 @@
|
||||||
/* make test_regexp */
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <regex.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
regex_t reg;
|
|
||||||
regmatch_t matches[2];
|
|
||||||
|
|
||||||
if (argc != 4) {
|
|
||||||
printf("Invoke with: test_regexp REGEXP REPLACE NR\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regcomp(®, argv[1], REG_EXTENDED) != 0) {
|
|
||||||
fprintf(stderr, "Regexp '%s' is not valid.\n", argv[1]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regexec(®, argv[3], 2, matches, 0) == 0 && matches[1].rm_eo != -1)
|
|
||||||
printf("New Number: %s%s\n", argv[2], &argv[3][matches[1].rm_so]);
|
|
||||||
else
|
|
||||||
printf("No match.\n");
|
|
||||||
|
|
||||||
regfree(®);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
#!/usr/bin/env python2.7
|
|
||||||
|
|
||||||
"""
|
|
||||||
AGPLv3+ 2016 Copyright Holger Hans Peter Freyther
|
|
||||||
|
|
||||||
Example of how to connect to the USSD side-channel and how to respond
|
|
||||||
with a fixed message.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
|
|
||||||
ussdSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
ussdSocket.connect(('127.0.0.1', 5001))
|
|
||||||
|
|
||||||
def send_dt1(dstref, data):
|
|
||||||
dlen = struct.pack('B', len(data)).encode('hex')
|
|
||||||
hex = '06' + dstref.encode('hex') + '00' + '01' + dlen + data.encode('hex')
|
|
||||||
pdata = hex.decode('hex')
|
|
||||||
out = struct.pack('>HB', len(pdata), 0xfd) + pdata
|
|
||||||
ussdSocket.send(out)
|
|
||||||
|
|
||||||
def send_rel(srcref, dstref):
|
|
||||||
hex = '04' + dstref.encode('hex') + srcref.encode('hex') + '000100'
|
|
||||||
pdata = hex.decode('hex')
|
|
||||||
out = struct.pack('>HB', len(pdata), 0xfd) + pdata
|
|
||||||
ussdSocket.send(out)
|
|
||||||
|
|
||||||
def recv_one():
|
|
||||||
plen = ussdSocket.recv(3)
|
|
||||||
(plen,ptype) = struct.unpack(">HB", plen)
|
|
||||||
data = ussdSocket.recv(plen)
|
|
||||||
|
|
||||||
return ptype, data
|
|
||||||
|
|
||||||
# Assume this is the ID request
|
|
||||||
data = ussdSocket.recv(4)
|
|
||||||
ussdSocket.send("\x00\x08\xfe\x05\x00" + "\x05\x01" + "ussd")
|
|
||||||
# ^len ^len of tag ... and ignore
|
|
||||||
|
|
||||||
# Expect a fake message. see struct ipac_msgt_sccp_state
|
|
||||||
ptype, data = recv_one()
|
|
||||||
print("%d %s" % (ptype, data.encode('hex')))
|
|
||||||
(srcref, dstref, transid, invokeid) = struct.unpack("<3s3sBB", data[1:9])
|
|
||||||
print("New transID %d invoke %d" % (transid, invokeid))
|
|
||||||
|
|
||||||
# Expect a the invocation.. todo.. extract invoke id
|
|
||||||
ptype, data = recv_one()
|
|
||||||
print("%d %s" % (ptype, data.encode('hex')))
|
|
||||||
|
|
||||||
# Reply with BSSAP + GSM 04.08 + MAP portion
|
|
||||||
# 00 == invoke id 0f == DCS
|
|
||||||
res = "01002a9b2a0802e1901c22a220020100301b02013b301604010f041155e7d2f9bc3a41412894991c06a9c9a713"
|
|
||||||
send_dt1(dstref, res.decode('hex'))
|
|
||||||
|
|
||||||
clear = "000420040109"
|
|
||||||
send_dt1(dstref, clear.decode('hex'))
|
|
||||||
|
|
||||||
# should be the clear complete
|
|
||||||
send_rel(srcref, dstref)
|
|
||||||
|
|
||||||
# Give it some time to handle connection shutdown properly
|
|
||||||
print("Gracefully sleeping")
|
|
||||||
import time
|
|
||||||
time.sleep(3)
|
|
|
@ -1,420 +0,0 @@
|
||||||
#!/usr/bin/env escript
|
|
||||||
%% -*- erlang -*-
|
|
||||||
%%! -smp disable
|
|
||||||
-module(gen_rtp_header).
|
|
||||||
|
|
||||||
% -mode(compile).
|
|
||||||
|
|
||||||
-define(VERSION, "0.1").
|
|
||||||
|
|
||||||
-export([main/1]).
|
|
||||||
|
|
||||||
-record(rtp_packet,
|
|
||||||
{
|
|
||||||
version = 2,
|
|
||||||
padding = 0,
|
|
||||||
marker = 0,
|
|
||||||
payload_type = 0,
|
|
||||||
seqno = 0,
|
|
||||||
timestamp = 0,
|
|
||||||
ssrc = 0,
|
|
||||||
csrcs = [],
|
|
||||||
extension = <<>>,
|
|
||||||
payload = <<>>,
|
|
||||||
realtime
|
|
||||||
}).
|
|
||||||
|
|
||||||
|
|
||||||
main(Args) ->
|
|
||||||
DefaultOpts = [{format, state},
|
|
||||||
{ssrc, 16#11223344},
|
|
||||||
{rate, 8000},
|
|
||||||
{pt, 98}],
|
|
||||||
{PosArgs, Opts} = getopts_checked(Args, DefaultOpts),
|
|
||||||
log(debug, fun (Dev) ->
|
|
||||||
io:format(Dev, "Initial options:~n", []),
|
|
||||||
dump_opts(Dev, Opts),
|
|
||||||
io:format(Dev, "~s: ~p~n", ["Args", PosArgs])
|
|
||||||
end, [], Opts),
|
|
||||||
main(PosArgs, Opts).
|
|
||||||
|
|
||||||
main([First | RemArgs], Opts) ->
|
|
||||||
try
|
|
||||||
F = list_to_integer(First),
|
|
||||||
Format = proplists:get_value(format, Opts, state),
|
|
||||||
PayloadData = proplists:get_value(payload, Opts, undef),
|
|
||||||
InFile = proplists:get_value(file, Opts, undef),
|
|
||||||
|
|
||||||
Payload = case {PayloadData, InFile} of
|
|
||||||
{undef, undef} ->
|
|
||||||
% use default value
|
|
||||||
#rtp_packet{}#rtp_packet.payload;
|
|
||||||
{P, undef} -> P;
|
|
||||||
{_, File} ->
|
|
||||||
log(info, "Loading file '~s'~n", [File], Opts),
|
|
||||||
{ok, InDev} = file:open(File, [read]),
|
|
||||||
DS = [ Pl#rtp_packet.payload || {_T, Pl} <- read_packets(InDev, Opts)],
|
|
||||||
file:close(InDev),
|
|
||||||
log(debug, "File '~s' closed, ~w packets read.~n", [File, length(DS)], Opts),
|
|
||||||
DS
|
|
||||||
end,
|
|
||||||
Dev = standard_io,
|
|
||||||
write_packet_pre(Dev, Format),
|
|
||||||
do_groups(Dev, Payload, F, RemArgs, Opts),
|
|
||||||
write_packet_post(Dev, Format),
|
|
||||||
0
|
|
||||||
catch
|
|
||||||
_:_ ->
|
|
||||||
log(debug, "~p~n", [hd(erlang:get_stacktrace())], Opts),
|
|
||||||
usage(),
|
|
||||||
halt(1)
|
|
||||||
end
|
|
||||||
;
|
|
||||||
|
|
||||||
main(_, _Opts) ->
|
|
||||||
usage(),
|
|
||||||
halt(1).
|
|
||||||
|
|
||||||
%%% group (count + offset) handling %%%
|
|
||||||
|
|
||||||
do_groups(_Dev, _Pl, _F, [], _Opts) ->
|
|
||||||
ok;
|
|
||||||
|
|
||||||
do_groups(Dev, Pl, F, [L], Opts) ->
|
|
||||||
do_groups(Dev, Pl, F, [L, 0], Opts);
|
|
||||||
|
|
||||||
do_groups(Dev, Pl, First, [L, O | Args], Opts) ->
|
|
||||||
Ssrc = proplists:get_value(ssrc, Opts, #rtp_packet.ssrc),
|
|
||||||
PT = proplists:get_value(pt, Opts, #rtp_packet.payload_type),
|
|
||||||
Len = list_to_num(L),
|
|
||||||
Offs = list_to_num(O),
|
|
||||||
log(info, "Starting group: Ssrc=~.16B, PT=~B, First=~B, Len=~B, Offs=~B~n",
|
|
||||||
[Ssrc, PT, First, Len, Offs], Opts),
|
|
||||||
Pkg = #rtp_packet{ssrc = Ssrc, payload_type = PT},
|
|
||||||
Pl2 = write_packets(Dev, Pl, Pkg, First, Len, Offs, Opts),
|
|
||||||
{Args2, Opts2} = getopts_checked(Args, Opts),
|
|
||||||
log(debug, fun (Io) ->
|
|
||||||
io:format(Io, "Changed options:~n", []),
|
|
||||||
dump_opts(Io, Opts2 -- Opts)
|
|
||||||
end, [], Opts),
|
|
||||||
do_groups(Dev, Pl2, First+Len, Args2, Opts2).
|
|
||||||
|
|
||||||
%%% error handling helpers %%%
|
|
||||||
|
|
||||||
getopts_checked(Args, Opts) ->
|
|
||||||
try
|
|
||||||
getopts(Args, Opts)
|
|
||||||
catch
|
|
||||||
C:R ->
|
|
||||||
log(error, "~s~n",
|
|
||||||
[explain_error(C, R, erlang:get_stacktrace(), Opts)], Opts),
|
|
||||||
usage(),
|
|
||||||
halt(1)
|
|
||||||
end.
|
|
||||||
|
|
||||||
explain_error(error, badarg, [{erlang,list_to_integer,[S,B]} | _ ], _Opts) ->
|
|
||||||
io_lib:format("Invalid number '~s' (base ~B)", [S, B]);
|
|
||||||
explain_error(error, badarg, [{erlang,list_to_integer,[S]} | _ ], _Opts) ->
|
|
||||||
io_lib:format("Invalid decimal number '~s'", [S]);
|
|
||||||
explain_error(C, R, [Hd | _ ], _Opts) ->
|
|
||||||
io_lib:format("~p, ~p:~p", [Hd, C, R]);
|
|
||||||
explain_error(_, _, [], _Opts) ->
|
|
||||||
"".
|
|
||||||
|
|
||||||
%%% usage and options %%%
|
|
||||||
|
|
||||||
myname() ->
|
|
||||||
filename:basename(escript:script_name()).
|
|
||||||
|
|
||||||
usage(Text) ->
|
|
||||||
io:format(standard_error, "~s: ~s~n", [myname(), Text]),
|
|
||||||
usage().
|
|
||||||
|
|
||||||
usage() ->
|
|
||||||
io:format(standard_error,
|
|
||||||
"Usage: ~s [Options] Start Count1 Offs1 [[Options] Count2 Offs2 ...]~n",
|
|
||||||
[myname()]).
|
|
||||||
|
|
||||||
show_version() ->
|
|
||||||
io:format(standard_io,
|
|
||||||
"~s ~s~n", [myname(), ?VERSION]).
|
|
||||||
|
|
||||||
show_help() ->
|
|
||||||
io:format(standard_io,
|
|
||||||
"Usage: ~s [Options] Start Count1 Offs1 [[Options] Count2 Offs2 ...]~n~n" ++
|
|
||||||
"Options:~n" ++
|
|
||||||
" -h, --help this text~n" ++
|
|
||||||
" --version show version info~n" ++
|
|
||||||
" -i, --file=FILE reads payload from file (state format by default)~n" ++
|
|
||||||
" -f, --frame-size=N read payload as binary frames of size N instead~n" ++
|
|
||||||
" -p, --payload=HEX set constant payload~n" ++
|
|
||||||
" --verbose=N set verbosity~n" ++
|
|
||||||
" -v increase verbosity~n" ++
|
|
||||||
" --format=state use state format for output (default)~n" ++
|
|
||||||
" -C, --format=c use simple C lines for output~n" ++
|
|
||||||
" --format=carray use a C array for output~n" ++
|
|
||||||
" -s, --ssrc=SSRC set the SSRC~n" ++
|
|
||||||
" -t, --type=N set the payload type~n" ++
|
|
||||||
" -r, --rate=N set the RTP rate [8000]~n" ++
|
|
||||||
" -D, --duration=N set the packet duration in RTP time units [160]~n" ++
|
|
||||||
" -d, --delay=FLOAT add offset to playout timestamp~n" ++
|
|
||||||
"~n" ++
|
|
||||||
"Arguments:~n" ++
|
|
||||||
" Start initial packet (sequence) number~n" ++
|
|
||||||
" Count number of packets~n" ++
|
|
||||||
" Offs timestamp offset (in RTP units)~n" ++
|
|
||||||
"", [myname()]).
|
|
||||||
|
|
||||||
getopts([ "--file=" ++ File | R], Opts) ->
|
|
||||||
getopts(R, [{file, File} | Opts]);
|
|
||||||
getopts([ "-i" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_arg("--file", T, R, Opts);
|
|
||||||
getopts([ "--frame-size=" ++ N | R], Opts) ->
|
|
||||||
Size = list_to_integer(N),
|
|
||||||
getopts(R, [{frame_size, Size}, {in_format, bin} | Opts]);
|
|
||||||
getopts([ "-f" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_arg("--frame-size", T, R, Opts);
|
|
||||||
getopts([ "--duration=" ++ N | R], Opts) ->
|
|
||||||
Duration = list_to_integer(N),
|
|
||||||
getopts(R, [{duration, Duration} | Opts]);
|
|
||||||
getopts([ "-D" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_arg("--duration", T, R, Opts);
|
|
||||||
getopts([ "--rate=" ++ N | R], Opts) ->
|
|
||||||
Rate = list_to_integer(N),
|
|
||||||
getopts(R, [{rate, Rate} | Opts]);
|
|
||||||
getopts([ "-r" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_arg("--rate", T, R, Opts);
|
|
||||||
getopts([ "--version" | _], _Opts) ->
|
|
||||||
show_version(),
|
|
||||||
halt(0);
|
|
||||||
getopts([ "--help" | _], _Opts) ->
|
|
||||||
show_help(),
|
|
||||||
halt(0);
|
|
||||||
getopts([ "-h" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_no_arg("--help", T, R, Opts);
|
|
||||||
getopts([ "--verbose=" ++ V | R], Opts) ->
|
|
||||||
Verbose = list_to_integer(V),
|
|
||||||
getopts(R, [{verbose, Verbose} | Opts]);
|
|
||||||
getopts([ "-v" ++ T | R], Opts) ->
|
|
||||||
Verbose = proplists:get_value(verbose, Opts, 0),
|
|
||||||
getopts_short_no_arg(T, R, [ {verbose, Verbose+1} | Opts]);
|
|
||||||
getopts([ "--format=state" | R], Opts) ->
|
|
||||||
getopts(R, [{format, state} | Opts]);
|
|
||||||
getopts([ "--format=c" | R], Opts) ->
|
|
||||||
getopts(R, [{format, c} | Opts]);
|
|
||||||
getopts([ "-C" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_no_arg("--format=c", T, R, Opts);
|
|
||||||
getopts([ "--format=carray" | R], Opts) ->
|
|
||||||
getopts(R, [{format, carray} | Opts]);
|
|
||||||
getopts([ "--payload=" ++ Hex | R], Opts) ->
|
|
||||||
getopts(R, [{payload, hex_to_bin(Hex)} | Opts]);
|
|
||||||
getopts([ "--ssrc=" ++ Num | R], Opts) ->
|
|
||||||
getopts(R, [{ssrc, list_to_num(Num)} | Opts]);
|
|
||||||
getopts([ "-s" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_arg("--ssrc", T, R, Opts);
|
|
||||||
getopts([ "--type=" ++ Num | R], Opts) ->
|
|
||||||
getopts(R, [{pt, list_to_num(Num)} | Opts]);
|
|
||||||
getopts([ "-t" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_arg("--type", T, R, Opts);
|
|
||||||
getopts([ "--delay=" ++ Num | R], Opts) ->
|
|
||||||
getopts(R, [{delay, list_to_float(Num)} | Opts]);
|
|
||||||
getopts([ "-d" ++ T | R], Opts) ->
|
|
||||||
getopts_alias_arg("--delay", T, R, Opts);
|
|
||||||
|
|
||||||
% parsing helpers
|
|
||||||
getopts([ "--" | R], Opts) ->
|
|
||||||
{R, normalize_opts(Opts)};
|
|
||||||
getopts([ O = "--" ++ _ | _], _Opts) ->
|
|
||||||
usage("Invalid option: " ++ O),
|
|
||||||
halt(1);
|
|
||||||
getopts([ [ $-, C | _] | _], _Opts) when C < $0; C > $9 ->
|
|
||||||
usage("Invalid option: -" ++ [C]),
|
|
||||||
halt(1);
|
|
||||||
|
|
||||||
getopts(R, Opts) ->
|
|
||||||
{R, normalize_opts(Opts)}.
|
|
||||||
|
|
||||||
getopts_short_no_arg([], R, Opts) -> getopts(R, Opts);
|
|
||||||
getopts_short_no_arg(T, R, Opts) -> getopts([ "-" ++ T | R], Opts).
|
|
||||||
|
|
||||||
getopts_alias_no_arg(A, [], R, Opts) -> getopts([A | R], Opts);
|
|
||||||
getopts_alias_no_arg(A, T, R, Opts) -> getopts([A, "-" ++ T | R], Opts).
|
|
||||||
|
|
||||||
getopts_alias_arg(A, [], [T | R], Opts) -> getopts([A ++ "=" ++ T | R], Opts);
|
|
||||||
getopts_alias_arg(A, T, R, Opts) -> getopts([A ++ "=" ++ T | R], Opts).
|
|
||||||
|
|
||||||
normalize_opts(Opts) ->
|
|
||||||
[ proplists:lookup(E, Opts) || E <- proplists:get_keys(Opts) ].
|
|
||||||
|
|
||||||
%%% conversions %%%
|
|
||||||
|
|
||||||
bin_to_hex(Bin) -> [hd(integer_to_list(N,16)) || <<N:4>> <= Bin].
|
|
||||||
hex_to_bin(Hex) -> << <<(list_to_integer([Nib],16)):4>> || Nib <- Hex>>.
|
|
||||||
|
|
||||||
list_to_num("-" ++ Str) -> -list_to_num(Str);
|
|
||||||
list_to_num("0x" ++ Str) -> list_to_integer(Str, 16);
|
|
||||||
list_to_num("0b" ++ Str) -> list_to_integer(Str, 2);
|
|
||||||
list_to_num(Str = [ $0 | _ ]) -> list_to_integer(Str, 8);
|
|
||||||
list_to_num(Str) -> list_to_integer(Str, 10).
|
|
||||||
|
|
||||||
%%% dumping data %%%
|
|
||||||
|
|
||||||
dump_opts(Dev, Opts) ->
|
|
||||||
dump_opts2(Dev, Opts, proplists:get_keys(Opts)).
|
|
||||||
|
|
||||||
dump_opts2(Dev, Opts, [OptName | R]) ->
|
|
||||||
io:format(Dev, " ~-10s: ~p~n",
|
|
||||||
[OptName, proplists:get_value(OptName, Opts)]),
|
|
||||||
dump_opts2(Dev, Opts, R);
|
|
||||||
dump_opts2(_Dev, _Opts, []) -> ok.
|
|
||||||
|
|
||||||
%%% logging %%%
|
|
||||||
|
|
||||||
log(L, Fmt, Args, Opts) when is_list(Opts) ->
|
|
||||||
log(L, Fmt, Args, proplists:get_value(verbose, Opts, 0), Opts).
|
|
||||||
|
|
||||||
log(debug, Fmt, Args, V, Opts) when V > 2 -> log2("DEBUG", Fmt, Args, Opts);
|
|
||||||
log(info, Fmt, Args, V, Opts) when V > 1 -> log2("INFO", Fmt, Args, Opts);
|
|
||||||
log(notice, Fmt, Args, V, Opts) when V > 0 -> log2("NOTICE", Fmt, Args, Opts);
|
|
||||||
log(warn, Fmt, Args, _V, Opts) -> log2("WARNING", Fmt, Args, Opts);
|
|
||||||
log(error, Fmt, Args, _V, Opts) -> log2("ERROR", Fmt, Args, Opts);
|
|
||||||
|
|
||||||
log(Lvl, Fmt, Args, V, Opts) when V >= Lvl -> log2("", Fmt, Args, Opts);
|
|
||||||
|
|
||||||
log(_, _, _, _i, _) -> ok.
|
|
||||||
|
|
||||||
log2(Type, Fmt, Args, _Opts) when is_list(Fmt) ->
|
|
||||||
io:format(standard_error, "~s: " ++ Fmt, [Type | Args]);
|
|
||||||
log2("", Fmt, Args, _Opts) when is_list(Fmt) ->
|
|
||||||
io:format(standard_error, Fmt, Args);
|
|
||||||
log2(_Type, Fun, _Args, _Opts) when is_function(Fun, 1) ->
|
|
||||||
Fun(standard_error).
|
|
||||||
|
|
||||||
%%% RTP packets %%%
|
|
||||||
|
|
||||||
make_rtp_packet(P = #rtp_packet{version = 2}) ->
|
|
||||||
<< (P#rtp_packet.version):2,
|
|
||||||
0:1, % P
|
|
||||||
0:1, % X
|
|
||||||
0:4, % CC
|
|
||||||
(P#rtp_packet.marker):1,
|
|
||||||
(P#rtp_packet.payload_type):7,
|
|
||||||
(P#rtp_packet.seqno):16,
|
|
||||||
(P#rtp_packet.timestamp):32,
|
|
||||||
(P#rtp_packet.ssrc):32,
|
|
||||||
(P#rtp_packet.payload)/bytes
|
|
||||||
>>.
|
|
||||||
|
|
||||||
parse_rtp_packet(
|
|
||||||
<< 2:2, % Version 2
|
|
||||||
0:1, % P (not supported yet)
|
|
||||||
0:1, % X (not supported yet)
|
|
||||||
0:4, % CC (not supported yet)
|
|
||||||
M:1,
|
|
||||||
PT:7,
|
|
||||||
SeqNo: 16,
|
|
||||||
TS:32,
|
|
||||||
Ssrc:32,
|
|
||||||
Payload/bytes >>) ->
|
|
||||||
#rtp_packet{
|
|
||||||
version = 0,
|
|
||||||
marker = M,
|
|
||||||
payload_type = PT,
|
|
||||||
seqno = SeqNo,
|
|
||||||
timestamp = TS,
|
|
||||||
ssrc = Ssrc,
|
|
||||||
payload = Payload}.
|
|
||||||
|
|
||||||
%%% payload generation %%%
|
|
||||||
|
|
||||||
next_payload(F) when is_function(F) ->
|
|
||||||
{F(), F};
|
|
||||||
next_payload({F, D}) when is_function(F) ->
|
|
||||||
{P, D2} = F(D),
|
|
||||||
{P, {F, D2}};
|
|
||||||
next_payload([P | R]) ->
|
|
||||||
{P, R};
|
|
||||||
next_payload([]) ->
|
|
||||||
undef;
|
|
||||||
next_payload(Bin = <<_/bytes>>) ->
|
|
||||||
{Bin, Bin}.
|
|
||||||
|
|
||||||
%%% real writing work %%%
|
|
||||||
|
|
||||||
write_packets(_Dev, DS, _P, _F, 0, _O, _Opts) ->
|
|
||||||
DS;
|
|
||||||
write_packets(Dev, DataSource, P = #rtp_packet{}, F, L, O, Opts) ->
|
|
||||||
Format = proplists:get_value(format, Opts, state),
|
|
||||||
Ptime = proplists:get_value(duration, Opts, 160),
|
|
||||||
Delay = proplists:get_value(delay, Opts, 0),
|
|
||||||
Rate = proplists:get_value(rate, Opts, 8000),
|
|
||||||
case next_payload(DataSource) of
|
|
||||||
{Payload, DataSource2} ->
|
|
||||||
write_packet(Dev, Ptime * F / Rate + Delay,
|
|
||||||
P#rtp_packet{seqno = F, timestamp = F*Ptime+O,
|
|
||||||
payload = Payload},
|
|
||||||
Format),
|
|
||||||
write_packets(Dev, DataSource2, P, F+1, L-1, O, Opts);
|
|
||||||
Other -> Other
|
|
||||||
end.
|
|
||||||
|
|
||||||
write_packet(Dev, Time, P = #rtp_packet{}, Format) ->
|
|
||||||
Bin = make_rtp_packet(P),
|
|
||||||
|
|
||||||
write_packet_line(Dev, Time, P, Bin, Format).
|
|
||||||
|
|
||||||
write_packet_pre(Dev, carray) ->
|
|
||||||
io:format(Dev,
|
|
||||||
"struct {float t; int len; char *data;} packets[] = {~n", []);
|
|
||||||
|
|
||||||
write_packet_pre(_Dev, _) -> ok.
|
|
||||||
|
|
||||||
write_packet_post(Dev, carray) ->
|
|
||||||
io:format(Dev, "};~n", []);
|
|
||||||
|
|
||||||
write_packet_post(_Dev, _) -> ok.
|
|
||||||
|
|
||||||
write_packet_line(Dev, Time, _P, Bin, state) ->
|
|
||||||
io:format(Dev, "~f ~s~n", [Time, bin_to_hex(Bin)]);
|
|
||||||
|
|
||||||
write_packet_line(Dev, Time, #rtp_packet{seqno = N, timestamp = TS}, Bin, c) ->
|
|
||||||
ByteList = [ [ $0, $x | integer_to_list(Byte, 16) ] || <<Byte:8>> <= Bin ],
|
|
||||||
ByteStr = string:join(ByteList, ", "),
|
|
||||||
io:format(Dev, "/* time=~f, SeqNo=~B, TS=~B */ {~s}~n", [Time, N, TS, ByteStr]);
|
|
||||||
|
|
||||||
write_packet_line(Dev, Time, #rtp_packet{seqno = N, timestamp = TS}, Bin, carray) ->
|
|
||||||
io:format(Dev, " /* RTP: SeqNo=~B, TS=~B */~n", [N, TS]),
|
|
||||||
io:format(Dev, " {~f, ~B, \"", [Time, size(Bin)]),
|
|
||||||
[ io:format(Dev, "\\x~2.16.0B", [Byte]) || <<Byte:8>> <= Bin ],
|
|
||||||
io:format(Dev, "\"},~n", []).
|
|
||||||
|
|
||||||
%%% real reading work %%%
|
|
||||||
|
|
||||||
read_packets(Dev, Opts) ->
|
|
||||||
Format = proplists:get_value(in_format, Opts, state),
|
|
||||||
|
|
||||||
read_packets(Dev, Opts, Format).
|
|
||||||
|
|
||||||
read_packets(Dev, Opts, Format) ->
|
|
||||||
case read_packet(Dev, Opts, Format) of
|
|
||||||
eof -> [];
|
|
||||||
Tuple -> [Tuple | read_packets(Dev, Opts, Format)]
|
|
||||||
end.
|
|
||||||
|
|
||||||
read_packet(Dev, Opts, bin) ->
|
|
||||||
Size = proplists:get_value(frame_size, Opts),
|
|
||||||
case file:read(Dev, Size) of
|
|
||||||
{ok, Data} -> {0, #rtp_packet{payload = iolist_to_binary(Data)}};
|
|
||||||
eof -> eof
|
|
||||||
end;
|
|
||||||
read_packet(Dev, _Opts, Format) ->
|
|
||||||
case read_packet_line(Dev, Format) of
|
|
||||||
{Time, Bin} -> {Time, parse_rtp_packet(Bin)};
|
|
||||||
eof -> eof
|
|
||||||
end.
|
|
||||||
|
|
||||||
read_packet_line(Dev, state) ->
|
|
||||||
case io:fread(Dev, "", "~f ~s") of
|
|
||||||
{ok, [Time, Hex]} -> {Time, hex_to_bin(Hex)};
|
|
||||||
eof -> eof
|
|
||||||
end.
|
|
|
@ -1,21 +0,0 @@
|
||||||
"
|
|
||||||
Simple UDP replay from the state files
|
|
||||||
"
|
|
||||||
|
|
||||||
PackageLoader fileInPackage: #Sockets.
|
|
||||||
FileStream fileIn: 'rtp_replay_shared.st'.
|
|
||||||
|
|
||||||
|
|
||||||
Eval [
|
|
||||||
| replay file host dport |
|
|
||||||
|
|
||||||
file := Smalltalk arguments at: 1 ifAbsent: [ 'rtpstream.state' ].
|
|
||||||
host := Smalltalk arguments at: 2 ifAbsent: [ '127.0.0.1' ].
|
|
||||||
dport := (Smalltalk arguments at: 3 ifAbsent: [ '4000' ]) asInteger.
|
|
||||||
sport := (Smalltalk arguments at: 4 ifAbsent: [ '0' ]) asInteger.
|
|
||||||
|
|
||||||
replay := RTPReplay on: file fromPort: sport.
|
|
||||||
|
|
||||||
Transcript nextPutAll: 'Going to stream now'; nl.
|
|
||||||
replay streamAudio: host port: dport.
|
|
||||||
]
|
|
|
@ -1,118 +0,0 @@
|
||||||
"
|
|
||||||
Simple UDP replay from the state files
|
|
||||||
"
|
|
||||||
|
|
||||||
PackageLoader fileInPackage: #Sockets.
|
|
||||||
|
|
||||||
Object subclass: SDPUtils [
|
|
||||||
"Look into using PetitParser."
|
|
||||||
SDPUtils class >> findPort: aSDP [
|
|
||||||
aSDP linesDo: [:line |
|
|
||||||
(line startsWith: 'm=audio ') ifTrue: [
|
|
||||||
| stream |
|
|
||||||
stream := line readStream
|
|
||||||
skip: 'm=audio ' size;
|
|
||||||
yourself.
|
|
||||||
^ Number readFrom: stream.
|
|
||||||
]
|
|
||||||
].
|
|
||||||
|
|
||||||
^ self error: 'Not found'.
|
|
||||||
]
|
|
||||||
|
|
||||||
SDPUtils class >> findHost: aSDP [
|
|
||||||
aSDP linesDo: [:line |
|
|
||||||
(line startsWith: 'c=IN IP4 ') ifTrue: [
|
|
||||||
| stream |
|
|
||||||
^ stream := line readStream
|
|
||||||
skip: 'c=IN IP4 ' size;
|
|
||||||
upToEnd.
|
|
||||||
]
|
|
||||||
].
|
|
||||||
|
|
||||||
^ self error: 'Not found'.
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
Object subclass: RTPReplay [
|
|
||||||
| filename socket |
|
|
||||||
RTPReplay class >> on: aFile [
|
|
||||||
^ self new
|
|
||||||
initialize;
|
|
||||||
file: aFile; yourself
|
|
||||||
]
|
|
||||||
|
|
||||||
RTPReplay class >> on: aFile fromPort: aPort [
|
|
||||||
^ self new
|
|
||||||
initialize: aPort;
|
|
||||||
file: aFile; yourself
|
|
||||||
]
|
|
||||||
|
|
||||||
initialize [
|
|
||||||
self initialize: 0.
|
|
||||||
]
|
|
||||||
|
|
||||||
initialize: aPort [
|
|
||||||
socket := Sockets.DatagramSocket local: '0.0.0.0' port: aPort.
|
|
||||||
]
|
|
||||||
|
|
||||||
file: aFile [
|
|
||||||
filename := aFile
|
|
||||||
]
|
|
||||||
|
|
||||||
localPort [
|
|
||||||
^ socket port
|
|
||||||
]
|
|
||||||
|
|
||||||
streamAudio: aHost port: aPort [
|
|
||||||
| file last_time last_image udp_send dest |
|
|
||||||
|
|
||||||
last_time := nil.
|
|
||||||
last_image := nil.
|
|
||||||
file := FileStream open: filename mode: #read.
|
|
||||||
|
|
||||||
"Send the payload"
|
|
||||||
dest := Sockets.SocketAddress byName: aHost.
|
|
||||||
udp_send := [:payload | | datagram |
|
|
||||||
datagram := Sockets.Datagram data: payload contents address: dest port: aPort.
|
|
||||||
socket nextPut: datagram
|
|
||||||
].
|
|
||||||
|
|
||||||
[file atEnd] whileFalse: [
|
|
||||||
| lineStream time data now_image |
|
|
||||||
lineStream := file nextLine readStream.
|
|
||||||
|
|
||||||
"Read the time, skip the blank, parse the data"
|
|
||||||
time := Number readFrom: lineStream.
|
|
||||||
lineStream skip: 1.
|
|
||||||
|
|
||||||
data := WriteStream on: (ByteArray new: 30).
|
|
||||||
[lineStream atEnd] whileFalse: [
|
|
||||||
| hex |
|
|
||||||
hex := lineStream next: 2.
|
|
||||||
data nextPut: (Number readFrom: hex readStream radix: 16).
|
|
||||||
].
|
|
||||||
|
|
||||||
last_time isNil
|
|
||||||
ifTrue: [
|
|
||||||
"First time, send it right now"
|
|
||||||
last_time := time.
|
|
||||||
last_image := Time millisecondClockValue.
|
|
||||||
udp_send value: data.
|
|
||||||
]
|
|
||||||
ifFalse: [
|
|
||||||
| wait_image new_image_time |
|
|
||||||
|
|
||||||
"How long to wait?"
|
|
||||||
wait_image := last_image + ((time - last_time) * 1000).
|
|
||||||
[ wait_image > Time millisecondClockValue ]
|
|
||||||
whileTrue: [Processor yield].
|
|
||||||
|
|
||||||
udp_send value: data.
|
|
||||||
last_time := time.
|
|
||||||
last_image := wait_image.
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
"""
|
|
||||||
Create a SIP connection and then stream...
|
|
||||||
"""
|
|
||||||
|
|
||||||
PackageLoader
|
|
||||||
fileInPackage: #OsmoSIP.
|
|
||||||
|
|
||||||
"Load for the replay code"
|
|
||||||
FileStream fileIn: 'rtp_replay_shared.st'.
|
|
||||||
|
|
||||||
|
|
||||||
Osmo.SIPCall subclass: StreamCall [
|
|
||||||
| sem stream |
|
|
||||||
|
|
||||||
createCall: aSDP [
|
|
||||||
| sdp |
|
|
||||||
stream := RTPReplay on: 'rtp_ssrc6976010.240.240.1_to_10.240.240.50.state'.
|
|
||||||
sdp := aSDP % {stream localPort}.
|
|
||||||
^ super createCall: sdp.
|
|
||||||
]
|
|
||||||
|
|
||||||
sem: aSemaphore [
|
|
||||||
sem := aSemaphore
|
|
||||||
]
|
|
||||||
|
|
||||||
sessionNew [
|
|
||||||
| host port |
|
|
||||||
Transcript nextPutAll: 'The call has started'; nl.
|
|
||||||
Transcript nextPutAll: sdp_result; nl.
|
|
||||||
|
|
||||||
host := SDPUtils findHost: sdp_result.
|
|
||||||
port := SDPUtils findPort: sdp_result.
|
|
||||||
|
|
||||||
[
|
|
||||||
stream streamAudio: host port: port.
|
|
||||||
Transcript nextPutAll: 'Streaming has finished.'; nl.
|
|
||||||
] fork.
|
|
||||||
]
|
|
||||||
|
|
||||||
sessionFailed [
|
|
||||||
sem signal
|
|
||||||
]
|
|
||||||
|
|
||||||
sessionEnd [
|
|
||||||
sem signal
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
Eval [
|
|
||||||
| transport agent call sem sdp_fr sdp_amr |
|
|
||||||
|
|
||||||
|
|
||||||
sdp_fr := (WriteStream on: String new)
|
|
||||||
nextPutAll: 'v=0'; cr; nl;
|
|
||||||
nextPutAll: 'o=twinkle 1739517580 1043400482 IN IP4 127.0.0.1'; cr; nl;
|
|
||||||
nextPutAll: 's=-'; cr; nl;
|
|
||||||
nextPutAll: 'c=IN IP4 127.0.0.1'; cr; nl;
|
|
||||||
nextPutAll: 't=0 0'; cr; nl;
|
|
||||||
nextPutAll: 'm=audio %1 RTP/AVP 0 101'; cr; nl;
|
|
||||||
nextPutAll: 'a=rtpmap:0 PCMU/8000'; cr; nl;
|
|
||||||
nextPutAll: 'a=rtpmap:101 telephone-event/8000'; cr; nl;
|
|
||||||
nextPutAll: 'a=fmtp:101 0-15'; cr; nl;
|
|
||||||
nextPutAll: 'a=ptime:20'; cr; nl;
|
|
||||||
contents.
|
|
||||||
|
|
||||||
sem := Semaphore new.
|
|
||||||
transport := Osmo.SIPUdpTransport
|
|
||||||
startOn: '0.0.0.0' port: 5066.
|
|
||||||
agent := Osmo.SIPUserAgent createOn: transport.
|
|
||||||
transport start.
|
|
||||||
|
|
||||||
call := (StreamCall
|
|
||||||
fromUser: 'sip:1000@sip.zecke.osmocom.org'
|
|
||||||
host: '127.0.0.1'
|
|
||||||
port: 5060
|
|
||||||
to: 'sip:123456@127.0.0.1'
|
|
||||||
on: agent)
|
|
||||||
sem: sem; yourself.
|
|
||||||
|
|
||||||
call createCall: sdp_fr.
|
|
||||||
|
|
||||||
|
|
||||||
"Wait for the stream to have ended"
|
|
||||||
sem wait.
|
|
||||||
|
|
||||||
(Delay forSeconds: 4) wait.
|
|
||||||
]
|
|
|
@ -1,28 +0,0 @@
|
||||||
print("Ni hao")
|
|
||||||
|
|
||||||
|
|
||||||
do
|
|
||||||
local tap = Listener.new("ip", "rtp")
|
|
||||||
local rtp_ssrc = Field.new("rtp.ssrc")
|
|
||||||
local frame_time = Field.new("frame.time_relative")
|
|
||||||
local rtp = Field.new("rtp")
|
|
||||||
|
|
||||||
function tap.packet(pinfo, tvb, ip)
|
|
||||||
local ip_src, ip_dst = tostring(ip.ip_src), tostring(ip.ip_dst)
|
|
||||||
local rtp_data = rtp()
|
|
||||||
local filename = "rtp_ssrc" .. rtp_ssrc() "_src_" .. ip_src .. "_to_" .. ip_dst .. ".state"
|
|
||||||
local f = io.open(filename, "a")
|
|
||||||
|
|
||||||
f:write(tostring(frame_time()) .. " ")
|
|
||||||
f:write(tostring(rtp_data.value))
|
|
||||||
f:write("\n")
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
function tap.draw()
|
|
||||||
print("DRAW")
|
|
||||||
end
|
|
||||||
function tap.reset()
|
|
||||||
print("RESET")
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,66 +0,0 @@
|
||||||
"I create output for some simple SQL statements for the HLR db"
|
|
||||||
|
|
||||||
|
|
||||||
Eval [
|
|
||||||
|
|
||||||
"Create tables if they don't exist"
|
|
||||||
Transcript show: 'CREATE TABLE SMS (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
created TIMESTAMP NOT NULL,
|
|
||||||
sent TIMESTAMP,
|
|
||||||
sender_id INTEGER NOT NULL,
|
|
||||||
receiver_id INTEGER NOT NULL,
|
|
||||||
deliver_attempts INTEGER NOT NULL DEFAULT 0,
|
|
||||||
valid_until TIMESTAMP,
|
|
||||||
reply_path_req INTEGER NOT NULL,
|
|
||||||
status_rep_req INTEGER NOT NULL,
|
|
||||||
protocol_id INTEGER NOT NULL,
|
|
||||||
data_coding_scheme INTEGER NOT NULL,
|
|
||||||
ud_hdr_ind INTEGER NOT NULL,
|
|
||||||
dest_addr TEXT,
|
|
||||||
user_data BLOB,
|
|
||||||
header BLOB,
|
|
||||||
text TEXT);'; nl;
|
|
||||||
show: 'CREATE TABLE Subscriber (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
created TIMESTAMP NOT NULL,
|
|
||||||
updated TIMESTAMP NOT NULL,
|
|
||||||
imsi NUMERIC UNIQUE NOT NULL,
|
|
||||||
name TEXT,
|
|
||||||
extension TEXT UNIQUE,
|
|
||||||
authorized INTEGER NOT NULL DEFAULT 0,
|
|
||||||
tmsi TEXT UNIQUE,
|
|
||||||
lac INTEGER NOT NULL DEFAULT 0);'; nl.
|
|
||||||
|
|
||||||
"Create some dummy subscribers"
|
|
||||||
num_sub := 1000.
|
|
||||||
num_sms := 30.
|
|
||||||
lac := 1.
|
|
||||||
|
|
||||||
Transcript show: 'BEGIN;'; nl.
|
|
||||||
|
|
||||||
1 to: num_sub do: [:each |
|
|
||||||
Transcript show: 'INSERT INTO Subscriber
|
|
||||||
(imsi, created, updated, authorized, lac, extension)
|
|
||||||
VALUES
|
|
||||||
(%1, datetime(''now''), datetime(''now''), 1, %2, %3);' %
|
|
||||||
{(274090000000000 + each). lac. each}; nl.
|
|
||||||
].
|
|
||||||
|
|
||||||
1 to: num_sms do: [:sms |
|
|
||||||
1 to: num_sub do: [:sub |
|
|
||||||
Transcript show: 'INSERT INTO SMS
|
|
||||||
(created, sender_id, receiver_id, valid_until,
|
|
||||||
reply_path_req, status_rep_req, protocol_id,
|
|
||||||
data_coding_scheme, ud_hdr_ind, dest_addr,
|
|
||||||
text) VALUES
|
|
||||||
(datetime(''now''), 1, %1, ''2222-2-2'',
|
|
||||||
0, 0, 0,
|
|
||||||
0, 0, ''123456'',
|
|
||||||
''abc'');' % {sub}; nl.
|
|
||||||
]
|
|
||||||
].
|
|
||||||
|
|
||||||
Transcript show: 'COMMIT;'; nl.
|
|
||||||
|
|
||||||
]
|
|
|
@ -1,10 +0,0 @@
|
||||||
"Query for one SMS"
|
|
||||||
|
|
||||||
Eval [
|
|
||||||
1 to: 100 do: [:each |
|
|
||||||
Transcript show: 'SELECT SMS.* FROM SMS
|
|
||||||
JOIN Subscriber ON SMS.receiver_id = Subscriber.id
|
|
||||||
WHERE SMS.id >= 1 AND SMS.sent IS NULL AND Subscriber.lac > 0
|
|
||||||
ORDER BY SMS.id LIMIT 1;'; nl.
|
|
||||||
].
|
|
||||||
]
|
|
|
@ -1,5 +0,0 @@
|
||||||
probe process("/usr/lib/libsqlite3.so.0.8.6").function("sqlite3_get_table")
|
|
||||||
{
|
|
||||||
a = user_string($zSql);
|
|
||||||
printf("sqlite3_get_table called '%s'\n", a);
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=OpenBSC MGCP
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
Restart=always
|
|
||||||
ExecStart=/usr/bin/osmo-bsc_mgcp -s -c /etc/osmocom/osmo-bsc-mgcp.cfg
|
|
||||||
RestartSec=2
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,12 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=OpenBSC BSC
|
|
||||||
Wants=osmo-bsc-mgcp.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
Restart=always
|
|
||||||
ExecStart=/usr/bin/osmo-bsc -c /etc/osmocom/osmo-bsc.cfg -s
|
|
||||||
RestartSec=2
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,12 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=Osmocom Gb proxy
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/usr/bin/osmo-gbproxy -c /etc/osmocom/osmo-gbproxy.cfg
|
|
||||||
Restart=always
|
|
||||||
RestartSec=2
|
|
||||||
RestartPreventExitStatus=1
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,11 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=OpenBSC Network In the Box (NITB)
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
Restart=always
|
|
||||||
ExecStart=/usr/bin/osmo-nitb -s -C -c /etc/osmocom/osmo-nitb.cfg -l /var/lib/osmocom/hlr.sqlite3
|
|
||||||
RestartSec=2
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,14 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=OpenBSC SGSN
|
|
||||||
Wants=osmo-hlr.service
|
|
||||||
After=osmo-hlr.service
|
|
||||||
After=osmo-hnbgw.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
Restart=always
|
|
||||||
ExecStart=/usr/bin/osmo-sgsn -c /etc/osmocom/osmo-sgsn.cfg
|
|
||||||
RestartSec=2
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,16 +0,0 @@
|
||||||
|
|
||||||
OBJS = testconv_main.o
|
|
||||||
|
|
||||||
CC = gcc
|
|
||||||
CFLAGS = -O0 -ggdb -Wall
|
|
||||||
LDFLAGS =
|
|
||||||
CPPFLAGS = -I../.. -I../../include $(shell pkg-config --cflags libosmocore) $(shell pkg-config --cflags libbcg729)
|
|
||||||
LIBS = ../../src/libmgcp/libmgcp.a ../../src/libcommon/libcommon.a $(shell pkg-config --libs libosmocore) $(shell pkg-config --libs libbcg729) -lgsm -lrt
|
|
||||||
|
|
||||||
testconv: $(OBJS)
|
|
||||||
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
testconv_main.o: testconv_main.c
|
|
||||||
|
|
||||||
$(OBJS):
|
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
|
|
|
@ -1,133 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <err.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/application.h>
|
|
||||||
|
|
||||||
#include <openbsc/debug.h>
|
|
||||||
#include <openbsc/gsm_data.h>
|
|
||||||
#include <openbsc/mgcp.h>
|
|
||||||
#include <openbsc/mgcp_internal.h>
|
|
||||||
|
|
||||||
#include "bscconfig.h"
|
|
||||||
#ifndef BUILD_MGCP_TRANSCODING
|
|
||||||
#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "openbsc/mgcp_transcode.h"
|
|
||||||
|
|
||||||
static int audio_name_to_type(const char *name)
|
|
||||||
{
|
|
||||||
if (!strcasecmp(name, "gsm"))
|
|
||||||
return 3;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
else if (!strcasecmp(name, "g729"))
|
|
||||||
return 18;
|
|
||||||
#endif
|
|
||||||
else if (!strcasecmp(name, "pcma"))
|
|
||||||
return 8;
|
|
||||||
else if (!strcasecmp(name, "l16"))
|
|
||||||
return 11;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst);
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
char buf[4096] = {0x80, 0};
|
|
||||||
int cc, rc;
|
|
||||||
struct mgcp_rtp_end *dst_end;
|
|
||||||
struct mgcp_rtp_end *src_end;
|
|
||||||
struct mgcp_trunk_config tcfg = {{0}};
|
|
||||||
struct mgcp_endpoint endp = {0};
|
|
||||||
struct mgcp_process_rtp_state *state;
|
|
||||||
int in_size;
|
|
||||||
int in_samples = 160;
|
|
||||||
int out_samples = 0;
|
|
||||||
uint32_t ts = 0;
|
|
||||||
uint16_t seq = 0;
|
|
||||||
|
|
||||||
osmo_init_logging(&log_info);
|
|
||||||
|
|
||||||
tcfg.endpoints = &endp;
|
|
||||||
tcfg.number_endpoints = 1;
|
|
||||||
endp.tcfg = &tcfg;
|
|
||||||
mgcp_initialize_endp(&endp);
|
|
||||||
|
|
||||||
dst_end = &endp.bts_end;
|
|
||||||
src_end = &endp.net_end;
|
|
||||||
|
|
||||||
if (argc <= 2)
|
|
||||||
errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16} [SPP]");
|
|
||||||
|
|
||||||
if ((src_end->codec.payload_type = audio_name_to_type(argv[1])) == -1)
|
|
||||||
errx(1, "invalid input format '%s'", argv[1]);
|
|
||||||
if ((dst_end->codec.payload_type = audio_name_to_type(argv[2])) == -1)
|
|
||||||
errx(1, "invalid output format '%s'", argv[2]);
|
|
||||||
if (argc > 3)
|
|
||||||
out_samples = atoi(argv[3]);
|
|
||||||
|
|
||||||
if (out_samples) {
|
|
||||||
dst_end->codec.frame_duration_den = dst_end->codec.rate;
|
|
||||||
dst_end->codec.frame_duration_num = out_samples;
|
|
||||||
dst_end->frames_per_packet = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = mgcp_transcoding_setup(&endp, dst_end, src_end);
|
|
||||||
if (rc < 0)
|
|
||||||
errx(1, "setup failed: %s", strerror(-rc));
|
|
||||||
|
|
||||||
state = dst_end->rtp_process_data;
|
|
||||||
OSMO_ASSERT(state != NULL);
|
|
||||||
|
|
||||||
in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
|
|
||||||
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
|
|
||||||
|
|
||||||
buf[1] = src_end->codec.payload_type;
|
|
||||||
*(uint16_t*)(buf+2) = htons(1);
|
|
||||||
*(uint32_t*)(buf+4) = htonl(0);
|
|
||||||
*(uint32_t*)(buf+8) = htonl(0xaabbccdd);
|
|
||||||
|
|
||||||
while ((cc = read(0, buf + 12, in_size))) {
|
|
||||||
int cont;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if (cc != in_size)
|
|
||||||
err(1, "read");
|
|
||||||
|
|
||||||
*(uint16_t*)(buf+2) = htonl(seq);
|
|
||||||
*(uint32_t*)(buf+4) = htonl(ts);
|
|
||||||
|
|
||||||
seq += 1;
|
|
||||||
ts += in_samples;
|
|
||||||
|
|
||||||
cc += 12; /* include RTP header */
|
|
||||||
|
|
||||||
len = cc;
|
|
||||||
|
|
||||||
do {
|
|
||||||
cont = mgcp_transcoding_process_rtp(&endp, dst_end,
|
|
||||||
buf, &len, sizeof(buf));
|
|
||||||
if (cont == -EAGAIN) {
|
|
||||||
fprintf(stderr, "Got EAGAIN\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cont < 0)
|
|
||||||
errx(1, "processing failed: %s", strerror(-cont));
|
|
||||||
|
|
||||||
len -= 12; /* ignore RTP header */
|
|
||||||
|
|
||||||
if (write(1, buf + 12, len) != len)
|
|
||||||
err(1, "write");
|
|
||||||
|
|
||||||
len = cont;
|
|
||||||
} while (len > 0);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
Call routing in OpenBSC
|
|
||||||
|
|
||||||
Flow of events:
|
|
||||||
|
|
||||||
# MO call initiated by MS, CHANNEL RQD, IMMEDIATE ASSIGN
|
|
||||||
# MS sends CC SETUP message, we assume already on TCH/H FACCH
|
|
||||||
# OpenBSC does a subscriber lookup based on the target extension
|
|
||||||
* If a subscriber is found:
|
|
||||||
# send CALL PROCEEDING message to MO
|
|
||||||
# page the MT subscriber and ask itI to ask for TCH/H
|
|
||||||
# once paging completes, we have the TCH/H for the MT end
|
|
||||||
# send SETUP to MT
|
|
||||||
# receive CALL CONFIRMED from MT
|
|
||||||
# set-up the TRAU mux mapping between the E1 subslots for both TCH/H
|
|
||||||
# receive ALERTING from MT, route ALERTING to MO
|
|
||||||
# receive CONNECT from MT, confirm to MT with CONNECT_ACK
|
|
||||||
# send a CONNECT message to MO, receive CONNECT_ACK from MO
|
|
||||||
* If subscriber is not found:
|
|
||||||
# send RELEASE COMPLETE with apropriate cause to MO (1: unalloacated 3: no route)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Thoughts about RR/MM:
|
|
||||||
|
|
||||||
* we allocate RR/MM entities on demand, when we need them
|
|
|
@ -1,172 +0,0 @@
|
||||||
E1 related data model
|
|
||||||
|
|
||||||
This data model describes the physical relationship of the individual
|
|
||||||
parts in the network, it is not the logical/protocol side of the GSM
|
|
||||||
network.
|
|
||||||
|
|
||||||
A BTS is connected to the BSC by some physical link. It could be an actual
|
|
||||||
E1 link, but it could also be abis-over-IP with a mixture of TCP and RTP/UDP.
|
|
||||||
|
|
||||||
To further complicate the fact, multiple BTS can share one such pysical
|
|
||||||
link. On a single E1 line, we can easily accomodate up to three BTS with
|
|
||||||
two TRX each.
|
|
||||||
|
|
||||||
Thus, it is best for OpenBSC to have some kind of abstraction layer. The BSC's
|
|
||||||
view of a BTS connected to it. We call this 'bts_link'. A bts_link can be
|
|
||||||
* all the TCP and UDP streams of a Abis-over-IP BTS
|
|
||||||
* a set of E1 timeslots for OML, RSL and TRAU connections on a E1 link
|
|
||||||
* a serial line exclusively used for OML messages (T-Link)
|
|
||||||
|
|
||||||
A bts_link can be registered with the OpenBSC core at runtime.
|
|
||||||
|
|
||||||
struct trx_link {
|
|
||||||
struct gsm_bts_trx *trx;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bts_link {
|
|
||||||
struct gsm_bts *bts;
|
|
||||||
struct trx_link trx_links[NUM_TRX];
|
|
||||||
};
|
|
||||||
|
|
||||||
Interface from stack to input core:
|
|
||||||
======================================================================
|
|
||||||
int abis_rsl_sendmsg(struct msgb *msg);
|
|
||||||
send a message through a RSL link to the TRX specified by the caller in
|
|
||||||
msg->trx.
|
|
||||||
|
|
||||||
int abis_rsl_rcvmsg(struct msgb *msg);
|
|
||||||
receive a message from a RSL link from the TRX specified by the
|
|
||||||
caller in msg->trx.
|
|
||||||
|
|
||||||
int abis_nm_sendmsg(struct msgb *msg);
|
|
||||||
send a message through a OML link to the BTS specified by the caller in
|
|
||||||
msg->trx->bts. The caller can just use bts->c0 to get the first TRX
|
|
||||||
in a BTS. (OML messages are not really sent to a TRX but to the BTS)
|
|
||||||
|
|
||||||
int abis_nm_rcvmsg(struct msgb *msg);
|
|
||||||
receive a message from a OML link from the BTS specified by the caller
|
|
||||||
in msg->trx->bts. The caller can just use bts->c0 to get the first
|
|
||||||
TRX in a BTS.
|
|
||||||
|
|
||||||
int abis_link_event(int event, void *data);
|
|
||||||
signal some event (such as layer 1 connect/disconnect) from the
|
|
||||||
input core to the stack.
|
|
||||||
|
|
||||||
int subch_demux_in(mx, const uint8_t *data, int len);
|
|
||||||
receive 'len' bytes from a given E1 timeslot (TRAU frames)
|
|
||||||
|
|
||||||
int subchan_mux_out(mx, uint8_t *data, int len);
|
|
||||||
obtain 'len' bytes of output data to be sent on E1 timeslot
|
|
||||||
|
|
||||||
Intrface by Input Core for Input Plugins
|
|
||||||
======================================================================
|
|
||||||
|
|
||||||
int btslink_register_plugin();
|
|
||||||
|
|
||||||
|
|
||||||
Configuration for the E1 input module
|
|
||||||
======================================================================
|
|
||||||
|
|
||||||
BTS
|
|
||||||
BTS number
|
|
||||||
number of TRX
|
|
||||||
OML link
|
|
||||||
E1 line number
|
|
||||||
timeslot number
|
|
||||||
[subslot number]
|
|
||||||
SAPI
|
|
||||||
TEI
|
|
||||||
for each TRX
|
|
||||||
RSL link
|
|
||||||
E1 line number
|
|
||||||
timeslot number
|
|
||||||
[subslot number]
|
|
||||||
SAPI
|
|
||||||
TEI
|
|
||||||
for each TS
|
|
||||||
E1 line number
|
|
||||||
timeslot number
|
|
||||||
subslot number
|
|
||||||
|
|
||||||
|
|
||||||
E1 input module data model
|
|
||||||
======================================================================
|
|
||||||
|
|
||||||
|
|
||||||
enum e1inp_sign_type {
|
|
||||||
E1INP_SIGN_NONE,
|
|
||||||
E1INP_SIGN_OML,
|
|
||||||
E1INP_SIGN_RSL,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct e1inp_sign_link {
|
|
||||||
/* list of signalling links */
|
|
||||||
struct llist_head list;
|
|
||||||
|
|
||||||
enum e1inp_sign_type type;
|
|
||||||
|
|
||||||
/* trx for msg->trx of received msgs */
|
|
||||||
struct gsm_bts_trx *trx;
|
|
||||||
|
|
||||||
/* msgb queue of to-be-transmitted msgs */
|
|
||||||
struct llist_head tx_list;
|
|
||||||
|
|
||||||
/* SAPI and TEI on the E1 TS */
|
|
||||||
uint8_t sapi;
|
|
||||||
uint8_t tei;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum e1inp_ts_type {
|
|
||||||
E1INP_TS_TYPE_NONE,
|
|
||||||
E1INP_TS_TYPE_SIGN,
|
|
||||||
E1INP_TS_TYPE_TRAU,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* A timeslot in the E1 interface */
|
|
||||||
struct e1inp_ts {
|
|
||||||
enum e1inp_ts_type type;
|
|
||||||
struct e1inp_line *line;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
struct llist_head sign_links;
|
|
||||||
} sign;
|
|
||||||
struct {
|
|
||||||
/* subchannel demuxer for frames from E1 */
|
|
||||||
struct subch_demux demux;
|
|
||||||
/* subchannel muxer for frames to E1 */
|
|
||||||
struct subch_mux mux;
|
|
||||||
} trau;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
/* mISDN driver has one fd for each ts */
|
|
||||||
struct osmo_fd;
|
|
||||||
} misdn;
|
|
||||||
} driver;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct e1inp_line {
|
|
||||||
unsigned int num;
|
|
||||||
char *name;
|
|
||||||
|
|
||||||
struct e1inp_ts ts[NR_E1_TS];
|
|
||||||
|
|
||||||
char *e1inp_driver;
|
|
||||||
void *driver_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Call from the Stack: configuration of this TS has changed */
|
|
||||||
int e1inp_update_ts(struct e1inp_ts *ts);
|
|
||||||
|
|
||||||
/* Receive a packet from the E1 driver */
|
|
||||||
int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
|
||||||
uint8_t tei, uint8_t sapi);
|
|
||||||
|
|
||||||
/* Send a packet, callback function in the driver */
|
|
||||||
int e1driver_tx_ts(struct e1inp_ts *ts, struct msgb *msg)
|
|
||||||
|
|
||||||
|
|
||||||
struct e1inp_driver {
|
|
||||||
const char *name;
|
|
||||||
int (*want_write)(struct e1inp_ts *ts);
|
|
||||||
};
|
|
|
@ -1,13 +0,0 @@
|
||||||
nat
|
|
||||||
bsc 0
|
|
||||||
token lol
|
|
||||||
location_area_code 1234
|
|
||||||
description bsc
|
|
||||||
max-endpoints 32
|
|
||||||
paging forbidden 0
|
|
||||||
bsc 1
|
|
||||||
token wat
|
|
||||||
location_area_code 5678
|
|
||||||
description bsc
|
|
||||||
max-endpoints 32
|
|
||||||
paging forbidden 0
|
|
|
@ -1,27 +0,0 @@
|
||||||
!
|
|
||||||
! Osmocom SGSN configuration
|
|
||||||
!
|
|
||||||
!
|
|
||||||
line vty
|
|
||||||
no login
|
|
||||||
!
|
|
||||||
sgsn
|
|
||||||
gtp local-ip 127.0.0.1
|
|
||||||
ggsn 0 remote-ip 127.0.0.2
|
|
||||||
ggsn 0 gtp-version 1
|
|
||||||
auth-policy accept-all
|
|
||||||
!
|
|
||||||
ns
|
|
||||||
timer tns-block 3
|
|
||||||
timer tns-block-retries 3
|
|
||||||
timer tns-reset 3
|
|
||||||
timer tns-reset-retries 3
|
|
||||||
timer tns-test 30
|
|
||||||
timer tns-alive 3
|
|
||||||
timer tns-alive-retries 10
|
|
||||||
encapsulation udp local-ip 127.0.0.1
|
|
||||||
encapsulation udp local-port 23000
|
|
||||||
encapsulation framerelay-gre enabled 0
|
|
||||||
!
|
|
||||||
bssgp
|
|
||||||
!
|
|
|
@ -1,54 +0,0 @@
|
||||||
according to GSM 05.02:
|
|
||||||
|
|
||||||
general parameters from CCCH:
|
|
||||||
* CA cell allocation of ARFCN's (System Information / BCCH)
|
|
||||||
* FN: TDMA frame number (t1,t2,t3') in SCH
|
|
||||||
|
|
||||||
specific parameters from channel assignment:
|
|
||||||
* MA: mobile allocation, defines set of ARFCN's, up to 64
|
|
||||||
* MAIO: index
|
|
||||||
* HSN: hopping sequence generator number (0..64)
|
|
||||||
|
|
||||||
|
|
||||||
hopping sequence generation (6.2.3):
|
|
||||||
|
|
||||||
uint8_t rntable[114] = {
|
|
||||||
48, 98, 63, 1, 36, 95, 78, 102, 94, 73,
|
|
||||||
0, 64, 25, 81, 76, 59, 124, 23, 104, 100,
|
|
||||||
101, 47, 118, 85, 18, 56, 96, 86, 54, 2,
|
|
||||||
80, 34, 127, 13, 6, 89, 57, 103, 12, 74,
|
|
||||||
55, 111, 75, 38, 109, 71, 112, 29, 11, 88,
|
|
||||||
87, 19, 3, 68, 110, 26, 33, 31, 8, 45,
|
|
||||||
82, 58, 40, 107, 32, 5, 106, 92, 62, 67,
|
|
||||||
77, 108, 122, 37, 60, 66, 121, 42, 51, 126,
|
|
||||||
117, 114, 4, 90, 43, 52, 53, 113, 120, 72,
|
|
||||||
16, 49, 7, 79, 119, 61, 22, 84, 9, 97,
|
|
||||||
125, 99, 17, 123
|
|
||||||
};
|
|
||||||
|
|
||||||
/* mai=0 represents lowest ARFCN in the MA */
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t hopping_mai(uint8_t hsn, uint32_t fn, uint8_t maio,
|
|
||||||
uint8_t t1, uint8_t t2, uint8_t t3_)
|
|
||||||
{
|
|
||||||
uint8_t mai;
|
|
||||||
|
|
||||||
if (hsn == 0) /* cyclic hopping */
|
|
||||||
mai = (fn + maio) % n;
|
|
||||||
else {
|
|
||||||
uint32_t m, m_, t_, s;
|
|
||||||
|
|
||||||
m = t2 + rntable[(hsn xor (t1 % 64)) + t3];
|
|
||||||
m_ = m % (2^NBIN);
|
|
||||||
t_ = t3 % (2^NBIN);
|
|
||||||
if (m_ < n then)
|
|
||||||
s = m_;
|
|
||||||
else
|
|
||||||
s = (m_ + t_) % n;
|
|
||||||
mai = (s + maio) % n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mai;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
Ideas about a handover algorithm
|
|
||||||
======================================================================
|
|
||||||
|
|
||||||
This is mostly based on the results presented in Chapter 8 of "Performance
|
|
||||||
Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen
|
|
||||||
and Joeroen Wigard.
|
|
||||||
|
|
||||||
|
|
||||||
=== Reasons for performing handover ===
|
|
||||||
|
|
||||||
Section 2.1.1: Handover used in their CAPACITY simulation:
|
|
||||||
|
|
||||||
1) Interference Handover
|
|
||||||
|
|
||||||
Average RXLEV is satisfactory high, but average RXQUAL too low indicates
|
|
||||||
interference to the channel. Handover should be made.
|
|
||||||
|
|
||||||
2) Bad Quality
|
|
||||||
|
|
||||||
Averaged RXQUAL is lower than a threshold
|
|
||||||
|
|
||||||
3) Low Level / Signal Strength
|
|
||||||
|
|
||||||
Average RXLEV is lower than a threshold
|
|
||||||
|
|
||||||
4) Distance Handover
|
|
||||||
|
|
||||||
MS is too far away from a cell (measured by TA)
|
|
||||||
|
|
||||||
5) Power budget / Better Cell
|
|
||||||
|
|
||||||
RX Level of neighbor cell is at least "HO Margin dB" dB better than the
|
|
||||||
current serving cell.
|
|
||||||
|
|
||||||
=== Ideal parameters for HO algorithm ===
|
|
||||||
|
|
||||||
Chapter 8, Section 2.2, Table 24:
|
|
||||||
|
|
||||||
Window RXLEV averaging: 10 SACCH frames (no weighting)
|
|
||||||
Window RXQUAL averaging: 1 SACCH frame (no averaging)
|
|
||||||
Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm
|
|
||||||
Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5
|
|
||||||
Interference Threshold: 1 of the last AV-RXLEV > -85 dBm &
|
|
||||||
3 of the last 4 AV-RXQUAL values >= 5
|
|
||||||
Power Budget: Level of neighbor cell > 3 dB better
|
|
||||||
Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?)
|
|
||||||
Distance Handover: Disabled
|
|
||||||
Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm
|
|
||||||
Evaluation rule 2: Level of candidate cell > 3dB better own cell
|
|
||||||
Timer Successful HO: 5 SACCH frames
|
|
||||||
Timer Unsuccessful HO: 1 SACCH frame
|
|
||||||
|
|
||||||
In a non-frequency hopping case, RXQUAL threshold can be decreased to
|
|
||||||
RXLEV >= 4
|
|
||||||
|
|
||||||
When frequency hopping is enabled, the following additional parameters
|
|
||||||
should be introduced:
|
|
||||||
|
|
||||||
* No intra-cell handover
|
|
||||||
* Use a HO Margin of 2dB
|
|
||||||
|
|
||||||
=== Handover Channel Reservation ===
|
|
||||||
|
|
||||||
In loaded network, each cell should reserve some channels for handovers,
|
|
||||||
rather than using all of them for new call establishment. This reduces the
|
|
||||||
need to drop calls due to failing handovers, at the expense of failing new call
|
|
||||||
attempts.
|
|
||||||
|
|
||||||
=== Dynamic HO Margin ===
|
|
||||||
|
|
||||||
The handover margin (hysteresis) should depend on the RXQUAL. Optimal results
|
|
||||||
were achieved with the following settings:
|
|
||||||
* RXQUAL <= 4: 9 dB
|
|
||||||
* RXQUAL == 5: 6 dB
|
|
||||||
* RXQUAL >= 6: 1 dB
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
== Actual Handover on a protocol level ==
|
|
||||||
|
|
||||||
After the BSC has decided a handover shall be done, it has to
|
|
||||||
|
|
||||||
# allocate a channel at the new BTS
|
|
||||||
# allocate a handover reference
|
|
||||||
# activate the channel on the BTS side using RSL CHANNEL ACTIVATION,
|
|
||||||
indicating the HO reference
|
|
||||||
# BTS responds with CHAN ACT ACK, including GSM frame number
|
|
||||||
# BSC sends 04.08 HO CMD to MS using old BTS
|
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
|
|
||||||
IPA SCCP message flow in the BSC
|
|
||||||
|
|
||||||
February, 2013 Holger Hans Peter Freyther
|
|
||||||
|
|
||||||
CONTENTS
|
|
||||||
|
|
||||||
1. SCCP inside the IPA header
|
|
||||||
2. Supported SCCP message types
|
|
||||||
3. Receiving SCCP messages
|
|
||||||
4. Sending SCCP messages
|
|
||||||
|
|
||||||
|
|
||||||
1. SCCP inside the IPA header
|
|
||||||
|
|
||||||
Many Soft-MSCs implement something that is called SCCP/lite. This means
|
|
||||||
that SCCP messages are transported inside a small multiplexing protocol
|
|
||||||
over TCP/IP. This is an alternative to a full SIGTRAN implementation.
|
|
||||||
|
|
||||||
The multiplexing protocol is the same as used with the sysmoBTS and the
|
|
||||||
ip.access nanoBTS. It is a three byte header with two bytes for the length
|
|
||||||
in network byte order and one byte for the type. The type to be used for
|
|
||||||
SCCP is 0xFD.
|
|
||||||
|
|
||||||
struct ipa_header {
|
|
||||||
uint16_t length_in_network_order;
|
|
||||||
uint8_t type;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2. Supported SCCP message types
|
|
||||||
|
|
||||||
To implement GSM 08.08 only a subset of SCCP messages need to be implemented.
|
|
||||||
For transporting paging and reset messages SCCP UDT messages are used. For
|
|
||||||
the connections with a Mobile Station (MS) a SCCP connection is opened. This
|
|
||||||
means that the SCCP CR, SCCP CC, SCCP CREF, SCCP RLC, SCCP RLSD, SCCP DT1
|
|
||||||
and SCCP IT messages are supported.
|
|
||||||
|
|
||||||
|
|
||||||
3. Receiving SCCP UDT messages
|
|
||||||
|
|
||||||
This is an illustration of the flow of messages. The IPA multiplexing protocol
|
|
||||||
is used for various protocols. This means there is a central place where the
|
|
||||||
multiplexing stream terminates. The stream is terminated in the osmo_bsc_msc.c
|
|
||||||
file and the ipaccess_a_fd_cb method. For SCCP messages the SCCP dispatching
|
|
||||||
sccp_system_incoming method is called. This function is implemented in the
|
|
||||||
libosmo-sccp library.
|
|
||||||
|
|
||||||
To receive UDT messages osmo_bsc_sccp.c:osmo_bsc_sccp_init is using the
|
|
||||||
sccp_set_read function to register a callback for UDT messages. The callback
|
|
||||||
is msc_sccp_read and it is calling bsc_handle_udt that is implemented in the
|
|
||||||
osmo_bsc_bssap.c. This function will handle the GSM 08.08 BSSAP messages.
|
|
||||||
Currently only the reset acknowledge and the paging messages are handled.
|
|
||||||
|
|
||||||
The BSC currently does not accept incoming SCCP messages and is only opening
|
|
||||||
SCCP connections to the MSC. When opening a connection the callbacks for state
|
|
||||||
changes (connection confirmed, released, release complete) are set and a routine
|
|
||||||
for handling incoming data. This registration is done in the osmo_bsc_sccp.c
|
|
||||||
file and the bsc_create_new_connection method. The name of the callback is
|
|
||||||
msc_outgoing_sccp_data and this will call bsc_handle_dt1 that is implemented
|
|
||||||
in the osmo_bsc_bssap.c file. This will forward the messages to the right
|
|
||||||
Mobile Station (MS).
|
|
||||||
|
|
||||||
|
|
||||||
4. Sending SCCP messages
|
|
||||||
|
|
||||||
There are three parts to sending that will be explained below. The first part
|
|
||||||
is to send an entire SCCP frame (which includes the GSM 08.08 data) to the
|
|
||||||
MSC. This is done by first registering the low level sending. sccp_system_init
|
|
||||||
is called with the function that is responsible for sending a message. The
|
|
||||||
msc_sccp_write_ipa will call the msc_queue_write function with the data and
|
|
||||||
the right MSC connection. Below the msc_queue_write the IPA header will be
|
|
||||||
prepended to the msg and then send to the MSC.
|
|
||||||
|
|
||||||
The BSC supports multiple different A-link connections, the decision to pick
|
|
||||||
the right MSC is done in this method. It is either done via the SCCP connection
|
|
||||||
or the ctx pointer.
|
|
||||||
|
|
||||||
When the BSC is starting a BSS RESET message will be sent to the MSC. The reset
|
|
||||||
is created in osmo_bsc_msc.c:initialize_if_needed and sccp_write is called with
|
|
||||||
the GSM 08.08 data and the connection to use. The libosmo-sccp library will
|
|
||||||
embed it into a SCCP UDT message and call the msc_sccp_write_ipa method.
|
|
||||||
|
|
||||||
When a new SCCP connection is to be created the bsc_create_new_connection
|
|
||||||
in the osmo_bsc_sccp.c file. The sccp_connection_socket method will create
|
|
||||||
the context for a SCCP connection. The state and data callback will be used
|
|
||||||
to be notified about data and changes. Once the connection is configured the
|
|
||||||
bsc_open_connection will be called that will ask the libosmo-sccp library to
|
|
||||||
create a SCCP CR message using the sccp_connection_connect method. For active
|
|
||||||
connections the sccp_connection_write method will be called.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
digraph G {
|
|
||||||
net [label="gsm_network"]
|
|
||||||
bts [label="gsm_bts"]
|
|
||||||
trx [label="gsm_bts_trx"]
|
|
||||||
ts [label="gsm_bts_trx_ts"]
|
|
||||||
lchan [label="gsm_lchan"]
|
|
||||||
sub [label="gsm_subscriber"]
|
|
||||||
subcon [label="gsm_subscriber_conn"]
|
|
||||||
sccpcon [label="osmo_bsc_sccp_con"]
|
|
||||||
subgrp [label="gsm_subscriber_group"]
|
|
||||||
|
|
||||||
net -> bts
|
|
||||||
bts -> trx
|
|
||||||
trx -> ts
|
|
||||||
ts -> lchan
|
|
||||||
|
|
||||||
lchan -> ts
|
|
||||||
ts -> trx
|
|
||||||
trx -> bts
|
|
||||||
bts -> net
|
|
||||||
|
|
||||||
lchan -> subcon
|
|
||||||
|
|
||||||
subcon -> sub
|
|
||||||
subcon -> sccpcon
|
|
||||||
subcon -> lchan
|
|
||||||
subcon -> lchan [label="ho_lchan"]
|
|
||||||
subcon -> bts
|
|
||||||
subcon -> lchan [label="secondary_lchan"]
|
|
||||||
|
|
||||||
sub -> subgrp
|
|
||||||
subgrp -> net
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
|
|
||||||
GSM Paging implementation in OpenBSC
|
|
||||||
|
|
||||||
== Code structure ==
|
|
||||||
|
|
||||||
The code is implemented in the libbsc/paging.c file. The external
|
|
||||||
interface is documented/specified in the include/openbsc/paging.h
|
|
||||||
header file. The code is used by the NITB and BSC application.
|
|
||||||
|
|
||||||
|
|
||||||
== Implementation ==
|
|
||||||
|
|
||||||
Paging can be initiated in two ways. The standard way is to page by
|
|
||||||
LAC. Each BTS has its own list/queue of outstanding paging operation.
|
|
||||||
When a subscriber is paged one "struct paging_request" per BTS will
|
|
||||||
be allocated and added to the tail of the list. The BTS is supposed
|
|
||||||
to be configured to not repeat the paging.
|
|
||||||
|
|
||||||
A paging_request will remain in the queue until a paging response or at
|
|
||||||
the expiry of the T3113. Every 500 milliseconds a RSL paging command is
|
|
||||||
send to the BTS. The 500 milliseconds is a throttling to not crash the
|
|
||||||
ip.access nanoBTS. Once one paging_request has been handled it will be
|
|
||||||
put at the end of the queue/list and the available slots for the BTS
|
|
||||||
will be decreased.
|
|
||||||
|
|
||||||
The available slots will be updated based on the paging load information
|
|
||||||
element of the CCCH Load indication. If no paging slots are considered
|
|
||||||
to be available and no load indication is sent a timer is started. The
|
|
||||||
current timeout is 500 milliseconds and at the expiry of the timer the
|
|
||||||
available slots will be set to 20.
|
|
||||||
|
|
||||||
OpenBSC has the " paging free <-1-1024>" configuration option. In case
|
|
||||||
there are less free channels than required no paging request will be
|
|
||||||
sent to the BTS. Instead it will be attempted to send the paging request
|
|
||||||
at the next timeout (500 milliseconds).
|
|
||||||
|
|
||||||
== Limitation ==
|
|
||||||
|
|
||||||
The paging throughput could be higher but this has lead to crashes on the
|
|
||||||
ip.access nanoBTS in the past.
|
|
||||||
|
|
||||||
== Configuration ==
|
|
||||||
|
|
||||||
=== ip.access nanoBTS ===
|
|
||||||
|
|
||||||
The current CCCH Load indication threshold is 10% and the period is 1 second.
|
|
||||||
The code can be found inside the src/libbsc/bts_ipaccess_nanobts.c inside the
|
|
||||||
nanobts_attr_bts array.
|
|
|
@ -14,24 +14,10 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
|
||||||
# Most systems won't be able to use these, so they're separated out
|
|
||||||
nitb_e1_configs = [
|
|
||||||
"doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg",
|
|
||||||
"doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg",
|
|
||||||
"doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg",
|
|
||||||
"doc/examples/osmo-nitb/bs11/openbsc.cfg",
|
|
||||||
"doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg",
|
|
||||||
"doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg",
|
|
||||||
"doc/examples/osmo-nitb/rbs2308/openbsc.cfg"
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
app_configs = {
|
app_configs = {
|
||||||
"msc": ["doc/examples/osmo-msc/osmo-msc.cfg"],
|
"msc": ["doc/examples/osmo-msc/osmo-msc.cfg"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
apps = [(4254, "src/osmo-msc/osmo-msc", "OsmoMSC", "msc"),
|
apps = [(4254, "src/osmo-msc/osmo-msc", "OsmoMSC", "msc"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue