YateBTS NiPC – Local Switching GSM Network

The Network in a PC (NiPC) application is a demo application written in Javascript that comes by default with YateBTS.

It was provided to ease the use of YateBTS as a small network and as an example on how to build applications based on YateBTS.

For a recap of the features implemented see Network in a PC page.

See Yate’s Javascript implementation for more information and examples.

Working mode GSM nipc enable

NiPC Setup

The NiPC application is composed of two Javascript scripts located in the nipc directory from YateBTS sources:

Note: Keep in mind that welcome.js can also be used in Roaming mode

javascript.conf

To set it up, edit javascript.conf.

[general]
routing=welcome.js

ybts.conf

You can enable NiPC by setting the mode=nipc in the [ybts] section. This is the default behaviour. If the ‘mode’ is not set, then the NiPC mode is loaded by default:

[ybts]
mode=nipc

extmodule.conf

When using 2G or 3G authentication set in extmodule.conf:

[scripts]
gsm_auth.sh=

subscribers.conf

Use the YateBTS LMI Web GUI to set subscribers or configure regexp.

From YateBTS machine access http://127.0.0.1/nib or http://ip_yatebts/nib from another server.

You can also edit subscribers.conf located together with Yate’s configuration files in /etc/yate or /usr/local/etc/yate but this is not recommended.

Required configurations in subscribers.conf:

You can’t use regexp and subscribers as the same time. If you configured a subscriber (even if it’s not active), regexp setting will be ignored.

This is an example of subscribers.conf with subscribers set individually:

; !!! NOTE !!!
; This file is used when YateBTS is in NiPC (Network in a PC) mode
; File generated by the YateBTS NiPC interface
[general]
; Your Country code (where YateBTS is installed)
; Ex: 1 for US, 44 for UK
country_code=40

; Subscribers are accepted by either matching the IMSI against this configured
; regular expression or by setting subscribers individually
; Note! If a regular expression is used, 2G/3G authentication cannot be used.
; Ex: regexp=^001
;regexp=^001

; you have to put subscriber IMSI as a category and the subscriber parameters
; as keys

[001990010001014]
; Oficial phone number. Should include configured country code
; Ex: msisdn=10744341111
msisdn=10744341111

; Whether this subscriber is allowed to use the service
; Allowed values: on, off
; Ex: active=off
active=on

; Card secrety. Set or generated when SIMs are written
; Ex:ki=00112233445566778899aabbccddeeff
ki=00112233445566778899aabbccddeeff

; Operator secret.
; Allowed values: empty for 2G IMSIs, 00000000000000000000000000000000 for 3G IMSIs.
; Ex: op=00000000000000000000000000000000

; SIM type
; Allowed values: 2G, 3G
; Ex: imsi_type=3G
imsi_type=2G

; Short number subscribers can use to dial each other. Can be empty or not set
; Ex:short_number=111
short_number=111

NiPC Commands

You can interact with the nipc.js script by using Yate’s Telnet interface. 5038 is Yate’s default rmanager port (port on which it accepts Telnet connections). Staring with version 2 you will see this information in the LMI Web GUI as well, but until then you can only retrieve them directly from Yate.

From the console:

telnet 127.0.0.1 5038

You can:

List registered users

nipc list registered

Example output:

IMSI MSISDN
————— —————
00101000000000 +3014567
00101001100110 +9233298
00101000000003 +3453456
00101000000002 +9999272

Reload subscribers.conf configurations

nipc reload

This will reload settings from subscribers.conf without losing existing registrations. (This was added in YateBTS 3)

List rejected IMSIs

nipc list rejected

Output example:

IMSI No attempts register
————— —————
10101000000000 1

List pending SMSs

nipc list sms

Output example:

FROM_IMSI FROM_MSISDN TO_IMSI TO_MSISDN
————— ————— ————— —————
00101000000000 +3014567 00101000000002 +9999272

Implementation

NOTE! This information is for developers.

You might also find this useful if you wish to modify the provided scripts. It’s not necessary to read it when you are just starting with YateBTS.

nipc.js

This scripts implements a basic HLR, MSC/VLR and SMSC for local users. It’s a global script.

Handled messages

To register and unregister users you need to handle user.register and user.unregister messages:

Message.install(onUnregister,”user.unregister”,80);
Message.install(onRegister,”user.register”,80);

To route calls between the local users and outside YateBTS you must handle the call.route message:

Message.install(onRoute,”call.route”,80);

To implement a small local SMSC:

Message.install(onIdleAction,”idle.execute”,110,”module”,”nipc_cache”);
Message.install(onSMS,”msg.execute”,80,”callto”,”nipc_smsc”);
Engine.setInterval(onInterval,1000);

To provide telnet commands:

Message.install(onCommand,”engine.command”,120);

When one of the messages handled by the script is received, the function associated with the handler will be called. For example, when the message call.route is received, the onRoute function is called.

The main points in detail

Although nipc.js is too large to be listed on the wiki, when inspecting the code you will notice various facts:

  • the IMSI is set in username parameter for user.register/user.unregister messages
  • you must return false to deny the registration (the message should not be handled even if you set the error)
  • call.route is used for routing calls, SMSs, USSDs. To distinguish between them, see route_type parameter. If the parameter is empty or missing, then it’s assumed you are routing a call
  • again, the IMSI is set in username parameter
  • depending on the lower level, the caller parameter can look like: ‘IMSI…..’. In this case, you should rewrite it to the real number associated to the IMSI before you finish routing
  • to finish routing a call (with or without an error) you must return true from the routing function. To leave the message to be handled in another module, use return false
  • the returned value should look like: ybts/IMSI00101000000000 or ybts/+998838838 (msisdn preceded by +) or ybts/IMEI….
  • to route SMS you must handle the call.route with route_type=msg. We simply return the nipc_smsc– string representing our SMSC (implemented in this scrip as well)
  • as you saw above, the script catches the msg.execute with priority 80 when callto=nipc_smsc.
  • SMSs are stored locally and, afterwards, we periodically try to deliver them until the maximum number of attempts is exceeded
  • the mobile originated SMSs come decoded from the lower layer. Assuming A sends a SMS to B:
    • the sms.called holds the real called number (B)
    • the caller parameter will look like IMSI….. (Ex: IMSI00101000000000) or +….. (subscriber msisdn). If it’s in the IMSI format it must be rewritten to msisdn before sending the Mobile terminated (MT) SMS
    • the called parameter holds the number of the SMSC (if set in the phone)
    • the text parameter holds the text of the SMS
  • when delivering an SMS, you must send the msg.execute message with following parameters:
    • caller – SMSC number
    • called – destination number (B in above example)
    • sms.caller – source number (A in above example)
    • text – the text of the message
    • callto – IMSI resource looking like ybts/IMSI……. (Ex: ybts/IMSI00101000000000)

welcome.js

welcome.js is a routing script, this is different from the global nipc.js presented above. Click on the link to read more about Javascript routing scripts.

When calling the number 32843(david), a welcomeIVR function is called. This will play a prompt and in case user presses:

function welcomeIVR(msg)
{
Engine.debug(Engine.DebugInfo,”Got call to welcome IVR.”);
Message.install(onChanDtmf, “chan.dtmf”, 90, “id”, msg.id);
Channel.callTo(“wave/play/”+getPathPrompt(“welcome.au”));

if (state == “”)
// No digit was pressed
// Wait aprox 10 seconds to see if digit is pressed
Channel.callTo(“wave/record/-“,{“maxlen”:180000});

Engine.debug(Engine.DebugInfo,”Returned to main function in state ‘”+state+”‘”);
if (state == “echoTest”)
Channel.callJust(“external/playrec/echo.sh”);
}

state = “”;
prompts_dir = “”;

Engine.debugName(“welcome”);

if (message.called==”32843″)
welcomeIVR(message);

This is the function that handles the received DTMFs.

function onChanDtmf(msg)
{
if(msg.text == 1) {
state = “echoTest”;
Channel.callTo(“wave/play/”+getPathPrompt(“echo.au”));
}
else if (msg.text == 2)
Channel.callJust(“conf/333”,{“lonely”:true});

else if (msg.text == 3)
Channel.callJust(“iax/iax:32843@83.166.206.79/32843”,{“caller”:”yatebts”});
//Channel.callJust(“iax/iax:090@192.168.1.1/090”,{“caller”:”yatebts”});
}