One More Digit Manipulation Case

Recently I’ve got an interesting task modify calling number on CUBE the following way:

An original calling number #919XXX3803

Had to be translated into 3XXX

The case could be interesting for those who like me struggle to understand better the regular expressions syntax.

Rule application

We have some outgoing dial-peer, that has outgoing translation profile configured:

dial-peer voice 777 voip
 description Test-Outgoing-DialPeer
 translation-profile outgoing Test-Translation-Profile

Translation profile has a rule configured for calling number manipulation:

voice translation-profile CUBE>IP-PBX
 translate calling 24

This rule is actually a set of regular expressions that calling number is checked against:

voice translation-rule 24
 rule 1 <regular expression>
 rule 2 <regular expression>

The regular expression format is the following:

rule 1 /match-pattern/ /replace-pattern/

The Match-Pattern

Again, we need to match #919XXX3803, where X is any single digit. The symbol for single digit in regular expression syntax is . (dot), so the string will be #919…3803

/#919...3803/

To be able to extract the digits contained in  and use them in the replace-pattern we have to put the dots in between brackets:

/#919(...)3803/

But the brackets are also a symbol to match in regular expression syntax. In order to have them treated as a separator we have to “escape” them with \ so the final match-pattern syntax will be

/#919\(...\)3803/

The Replace-Pattern

The replace pattern will be just those three digits that we get from the middle of matched number with 3 prepended. Match-pattern fragments that we put in brackets are numbered sequentially (1,2,3…) to place them in replace-pattern. The part we need is the first (and the only) one, so it will get number 1.  With 3 prefix the Replace-Pattern should look like:

/31/

But if we put it this way, 1 will just mean the 1 digit, not the match-pattern fragment and as a result we will get 31, not 3XXX we want. 1 should be escaped with \ so the replace-pattern will look the following way:

/3\1/

The whole translation rule looks like this:

voice translation-rule 24
 rule 1 /^\#919\(...\)3803/ /3\1/

Verification

The first and easiest thing to do is to execute test command and check the original and translated numbers:

#test voice translation-rule 24 #9197773803
Matched with rule 1
Original number: #9197773803    Translated number: 3777
Original number type: none      Translated number type: none
Original number plan: none      Translated number plan: none

Then run debug voip translation command, make a call and check the output:

2091546: //-1/3CE0EAE998BB/RXRULE/regxrule_profile_translate_internal: number=#9197773803 type=unknown plan=unknown numbertype=calling
2091547: //-1/3CE0EAE998BB/RXRULE/regxrule_profile_match_internal: Matched with rule 1 in ruleset 24
2091548: //-1/3CE0EAE998BB/RXRULE/regxrule_profile_match_internal: Matched with rule 1 in ruleset 24
2091549: //-1/3CE0EAE998BB/RXRULE/sed_subst: Successful substitution; pattern=#9197773803 matchPattern=^\#919(...)3803 replacePattern=3\1 replaced pattern=3777
2091550: //-1/3CE0EAE998BB/RXRULE/regxrule_subst_num_type: Match Type = none, Replace Type = none Input Type = unknown
2091551: //-1/3CE0EAE998BB/RXRULE/regxrule_subst_num_plan: Match Plan = none, Replace Plan = none Input Plan = unknown
2091552: //-1/3CE0EAE998BB/RXRULE/regxrule_profile_translate_internal: xlt_number=3777 xlt_type=unknown xlt_plan=unknown

Note, that if you run debug voip translation an then execute test command, apart from giving you the test result, this command also produces debug output:

ZA-IP_IPGW2-DC2#debug voip translation
VoIP Translation Rule debugging is enabledZA-IP_IPGW2-DC2#test voice translation-rule 24 #9198883803
Matched with rule 1
Original number: #9198883803    Translated number: 3888
Original number type: none      Translated number type: none
Original number plan: none      Translated number plan: noneZA-IP_IPGW2-DC2#sh logg
2092003: Jan 19 17:30:01.529: //-1/xxxxxxxxxxxx/RXRULE/sed_subst: Successful substitution; pattern=#9198883803 matchPattern=^\#919(...)3803 replacePattern=3\1 replaced pattern=3888
2092004: Jan 19 17:30:01.529: //-1/xxxxxxxxxxxx/RXRULE/regxrule_subst_num_type: Match Type = none, Replace Type = none Input Type = none
2092005: Jan 19 17:30:01.529: //-1/xxxxxxxxxxxx/RXRULE/regxrule_subst_num_plan: Match Plan = none, Replace Plan = none Input Plan = none

Transcoding on Cisco Unified Border Element

Previously to use hardware (PVDM-based) transcoding you needed to register DSPFarm on CUCM or CME. But what if you don’t use any of these call control platforms, just have a router working as CUBE and want to accept one call leg and set up another with a codec different from originating? Starting with CUBE 9.0 Cisco has introduced Local Transcoding Interface (LTI) feature that permits you to register transcoding DSPFarm profile on CUBE itself.

CUBE version is mapped to IOS version, some information on version correspondence can be found here. The exact CUBE version on your router can be verified with show cube status command.

The call flow I used to test the feature is the following:

SIPCallFlow

Here is a configuration example:

voice-card 0
dsp services dspfarmdspfarm profile 1 transcode universal
codec g729r8
codec g729br8
codec g711ulaw
codec g711alaw
codec g729ar8
codec g729abr8
maximum sessions 4
associate application CUBE <--- this command makes the profile to register and interact with CUBE
no shutdown <--- Don't forget this, the profile is shutdown by default 

First, checking that DSPFarm profile has registered successfully:

#show dspfarm profile
Dspfarm Profile ConfigurationProfile ID = 1, Service =Universal TRANSCODING, Resource ID = 1
Profile Description :
Profile Service Mode : Non Secure
Profile Admin State : UP
Profile Operation State : ACTIVE
Application : CUBE Status : ASSOCIATED
Resource Provider : FLEX_DSPRM Status : UP
Number of Resource Configured : 4
Number of Resources Out of Service : 0
Number of Resources Active : 0
Codec Configuration: num_of_codecs:6
Codec : g729r8, Maximum Packetization Period : 60
Codec : g729br8, Maximum Packetization Period : 60
Codec : g711ulaw, Maximum Packetization Period : 30
Codec : g711alaw, Maximum Packetization Period : 30
Codec : g729ar8, Maximum Packetization Period : 60
Codec : g729abr8, Maximum Packetization Period : 60

Then trying to establish the call.

Verification

After the remote party answered the call, we can see RTP streams for both call legs up:

#sh voip rtp connections
VoIP RTP Port Usage Information:
Max Ports Available: 8091, Ports Reserved: 101, Ports in Use: 2
Port range not configured, Min: 16384, Max: 32767Ports Ports Ports
Media-Address Range Available Reserved In-useDefault Address-Range 8091 101 2VoIP RTP active connections :
No. CallId dstCallId LocalRTP RmtRTP LocalIP RemoteIP
1 510647 510648 17882 10012 X.X.X.6 X.X.X.1
2 510648 510647 17884 12818 Y.Y.Y.68 Y.Y.Y.147
Found 2 active RTP connections

It doesn’t tell us though which codec both call legs are using. Show call active voice command does that:

#show call active voice brief

Telephony call-legs: 0
SIP call-legs: 2
H323 call-legs: 0
Call agent controlled call-legs: 0
SCCP call-legs: 0
Multicast call-legs: 0
Total call-legs: 2
2CD9 : 510647 307788128ms.1 () +16290 pid:520 Answer 3309 active
dur 00:00:36 tx:1831/292960 rx:1826/292160 dscp:0 media:0 audio tos:0xB8 video tos:0x0
IP X.X.X.1:10012 SRTP: off rtt:0ms pl:0/0ms lost:0/0/0 delay:0/0/0ms g711alaw TextRelay: off Transcoded: Yes
media inactive detected:n media contrl rcvd:n/a timestamp:n/a
long duration call detected:n long duration call duration:n/a timestamp:n/a

2CD9 : 510648 307788138ms.1 () +16270 pid:521 Originate 3000 active
dur 00:00:36 tx:1825/36500 rx:1831/36620 dscp:0 media:0 audio tos:0xB8 video tos:0x0
IP Y.Y.Y.147:12818 SRTP: off rtt:0ms pl:0/0ms lost:0/0/0 delay:0/0/0ms g729r8 TextRelay: off Transcoded: Yes
media inactive detected:n media contrl rcvd:n/a timestamp:n/a
long duration call detected:n long duration call duration:n/a timestamp:n/a

Here are the active sessions on configured DSPFarm:

#show dspfarm all
Dspfarm Profile ConfigurationProfile ID = 1, Service =Universal TRANSCODING, Resource ID = 1
Profile Description :
Profile Service Mode : Non Secure
Profile Admin State : UP
Profile Operation State : ACTIVE
Application : CUBE Status : ASSOCIATED
Resource Provider : FLEX_DSPRM Status : UP
Number of Resource Configured : 4
Number of Resources Out of Service : 0
Number of Resources Active : 1
Codec Configuration: num_of_codecs:6
Codec : g729r8, Maximum Packetization Period : 60
Codec : g729br8, Maximum Packetization Period : 60
Codec : g711ulaw, Maximum Packetization Period : 30
Codec : g711alaw, Maximum Packetization Period : 30
Codec : g729ar8, Maximum Packetization Period : 60
Codec : g729abr8, Maximum Packetization Period : 60
SLOT DSP VERSION STATUS CHNL USE TYPE RSC_ID BRIDGE_ID PKTS_TXED PKTS_RXED0 1 33.1.0 UP 1 USED xcode 1 510648 799 807
0 1 33.1.0 UP 1 USED xcode 1 510647 804 797
0 1 33.1.0 UP N/A FREE xcode 1 - - -
0 1 33.1.0 UP N/A FREE xcode 1 - - -
0 1 33.1.0 UP N/A FREE xcode 1 - - -Total number of DSPFARM DSP channel(s) 4

Codec negotiation process

All the output below is taken using debug ccsip messages. Messages not related to codec negotiation are omitted.

First, CUBE receives SIP INVITE from Local IP PBX with SDP containing G.711 A-law and µ-law:

Received:
INVITE sip:3000@x.x.x.6 SIP/2.0
Via: SIP/2.0/UDP x.x.x.1:5060;branch=z9hG4bK23d6e61b;rport
From: "3309" <sip:3309@pbx.local>;tag=as3d61976b
To: <sip:3000@x.x.x.6>
Contact: <sip:3309@x.x.x.1>
Call-ID: 763b91d53953a6f118dda08347245740@pbx.local
CSeq: 102 INVITE
User-Agent: Local PBX
Max-Forwards: 70
Date:
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO
Supported: replaces
Content-Type: application/sdp
Content-Length: 235
v=0
o=root 4037 4037 IN IP4 X.X.X.1
s=session
c=IN IP4 X.X.X.1
t=0 0
m=audio 10012 RTP/AVP 8 0 101
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=ptime:20
a=sendrecv

Then it resends the INVITE to Remote IP PBX proposing the same codecs:

Sent:
INVITE sip:3000@Y.Y.Y.147:5060 SIP/2.0
Via: SIP/2.0/UDP Y.Y.Y.68:5060;branch=z9hG4bK24310A3
Remote-Party-ID: "3309" <sip:3309@Y.Y.Y.68>;party=calling;screen=no;privacy=off
From: "3309" <sip:3309@Y.Y.Y.68>;tag=12587968-2B6
To: <sip:3000@Y.Y.Y.147>
Date: Fri, 08 Jan 2016 15:14:29 GMT
Call-ID: 58D34E27-B55111E5-9AFACBDC-E71387B6@Y.Y.Y.68
Supported: 100rel,timer,resource-priority,replaces,sdp-anat
Min-SE: 1800
Cisco-Guid: 1490163159-3041989093-2599734236-3876816822
User-Agent: Cisco-SIPGateway
Allow: INVITE, OPTIONS, BYE, CANCEL, ACK, PRACK, UPDATE, REFER, SUBSCRIBE, NOTIFY, INFO, REGISTER
CSeq: 101 INVITE
Timestamp: 1452266069
Contact: <sip:3309@Y.Y.Y.68:5060>
Call-Info: <sip:Y.Y.Y.68:5060>;method="NOTIFY;Event=telephone-event;Duration=2000"
Expires: 180
Allow-Events: telephone-event
Max-Forwards: 69
Content-Type: application/sdp
Content-Disposition: session;handling=required
Content-Length: 289v=0
o=CiscoSystemsSIP-GW-UserAgent 3992 8898 IN IP4 Y.Y.Y.68
s=SIP Call
c=IN IP4 Y.Y.Y.68
t=0 0
m=audio 17884 RTP/AVP 8 0 101 19
c=IN IP4 Y.Y.Y.68
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=rtpmap:19 CN/8000

The Remote PBX responds with an error message:

Received:
SIP/2.0 488 Not acceptable here
Via: SIP/2.0/UDP Y.Y.Y.68:5060;branch=z9hG4bK24310A3;received=Y.Y.Y.68
From: "3309" <sip:3309@Y.Y.Y.68>;tag=12587968-2B6
To: <sip:3000@Y.Y.Y.147>;tag=as55df16aa
Call-ID: 58D34E27-B55111E5-9AFACBDC-E71387B6@Y.Y.Y.68
CSeq: 101 INVITE
Server: Remote PBX
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces, timer
Content-Length: 0

The response means that some media capability (we know it’s a codec) is not acceptable at the remote end. RFC3261 says this type of message MAY include a description of media capabilities that weren’t accepted so this could have told us what exactly the remote end didn’t like, but unfortunately in this case it doesn’t.

Anyway, CUBE sends another INVITE message, proposing G.729 codec:

Sent:
INVITE sip:3000@Y.Y.Y.147:5060 SIP/2.0
Via: SIP/2.0/UDPY.Y.Y.68:5060;branch=z9hG4bK244210D
Remote-Party-ID: "3309" <sip:3309@Y.Y.Y.68>;party=calling;screen=no;privacy=off
From: "3309" <sip:%239193093803@Y.Y.Y.68>;tag=12587AFC-2DC
To: <sip:3000@Y.Y.Y.147>
Date: Fri, 08 Jan 2016 15:14:29 GMT
Call-ID: 58D34E27-B55111E5-9AFACBDC-E71387B6@Y.Y.Y.68
Supported: 100rel,timer,resource-priority,replaces,sdp-anat
Min-SE: 1800
Cisco-Guid: 1490163159-3041989093-2599734236-3876816822
User-Agent: Cisco-SIPGateway
Allow: INVITE, OPTIONS, BYE, CANCEL, ACK, PRACK, UPDATE, REFER, SUBSCRIBE, NOTIFY, INFO, REGISTER
CSeq: 102 INVITE
Timestamp: 1452266069
Contact: <sip:3309@Y.Y.Y.68:5060>
Call-Info: <sip:Y.Y.Y.68:5060>;method="NOTIFY;Event=telephone-event;Duration=2000"
Expires: 180
Allow-Events: telephone-event
Max-Forwards: 69
Content-Type: application/sdp
Content-Disposition: session;handling=required
Content-Length: 337v=0
o=CiscoSystemsSIP-GW-UserAgent 3992 8899 IN IP4 Y.Y.Y.68
s=SIP Call
c=IN IP4 Y.Y.Y.68
t=0 0
m=audio 17884 RTP/AVP 8 0 18 101 19
c=IN IP4 Y.Y.Y.68
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:18 G729/8000
a=fmtp:18 annexb=yes
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=rtpmap:19 CN/8000

which remote PBX accepts sending an OK response with acceptable media capabilities list including G.729. This actually happens when remote user picks up the phone. Here is the message:

Received:
SIP/2.0 200 OK
Via: SIP/2.0/UDP Y.Y.Y.68:5060;branch=z9hG4bK244210D;received=Y.Y.Y.68From: "3309" <sip:3309@Y.Y.Y.68>;tag=12587AFC-2DC
To: <sip:3000@Y.Y.Y.147>;tag=as25e69ab7
Call-ID: 58D34E27-B55111E5-9AFACBDC-E71387B6@Y.Y.Y.68
CSeq: 102 INVITE
Server: Remote PBX
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces, timer
Session-Expires: 1800;refresher=uas
Contact: <sip:3000@Y.Y.Y.147:5060>
Content-Type: application/sdp
Require: timer
Content-Length: 263v=0
o=root 1736022661 1736022661 IN IP4 Y.Y.Y.147
s=Asterisk PBX 11.14.2
c=IN IP4 Y.Y.Y.147
t=0 0
m=audio 12818 RTP/AVP 18 101
a=rtpmap:18 G729/8000
a=fmtp:18 annexb=no
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=ptime:20
a=sendrecv

The call proceeds with two legs using different codecs as expected.

The whole flow looks the following way (Red bars mark messages listed above, the ones containing codecs information):

CallDiagram

An error 488 Not acceptable here could be avoided by changing the outgoing dial-peer configuration. CUBE proposes G.711 codecs first because of the voice-class applied to its outgoing dial-peer. This voice-class lists the codecs with the following preference order:

voice class codec 3
codec preference 1 g711alaw
codec preference 2 g711ulaw
codec preference 3 g729r8
codec preference 4 g729br8

Changing the priority or leaving just G.729 will do the trick.