Interop Cisco Unified Call Manager with Microsoft Teams

A common topic of conversation with our Cisco Call Manager (“CUCM”) customers has been whether or not Microsoft Teams can act as a softphone for CUCM.

The answer is … sort of.

Let’s look at the process of getting this working. First and foremost, at the time of this writing, calling from CUCM to MS Teams directly is not supported by Cisco – so don’t expect to call TAC if you have problems. Best we can tell, Microsoft doesn’t seem to care what 3rd-party PBX you’re using as long as you’re using a supported Session Border Controller (SBC).

What Cisco does support is using their SBC – the Cisco Unified Border Element (“CUBE”) – as an intermediary between a PSTN provider (ex: a SIP carrier) and MS Teams (Microsoft refers to this as “Direct Routing”).

The CUBE/Microsoft configuration is documented here.

Aside from being quite a long read and somewhat difficult to re-type, it does work generally as directed. The supported equipment CUBE models are an ISR4K or CSR1K running IOS 17.2.1r or IOS 17.3.2 (We built our installation on IOS 17.2.1r).

Getting Started


As mentioned, Cisco won’t support it, but there’s nothing stopping substituting the “PSTN provider” for CUCM. Our goal is to reproduce as close to a “softphone experience” for CUCM as possible, using MS Teams as the “softphone”.


There are a few items of note that aren’t particularly obvious from the document that are worth calling out:

  • Be sure to follow the certificate installation very carefully. While we’ve been deploying SIP TLS and SRTP for years, it’s usually involved self-signed certs or pre-shared keys. We had our security CCIEs handle the cert installations, and even they had to do it a couple times over to get it just as the document indicates. The document’s process is correct, just be mindful.
  • Copying the config out of the PDF is problematic. This may seem obvious, but it’s possible to break the SRTP negotiation in such a way that is impossible to fix from debugs: parts of the SRTP negotiation is obfuscated in the debugs for security reasons, and you literally can’t see your errors.
  • This config is complicated enough that you’ll end up needing to do some debugging – getting it right on the first shot is slim. Where to start? OPTIONS ping

Microsoft requires the CUBE to initiate the OPTIONS pings. If you’re configured correctly, Microsoft will respond, and your dial-peers will come up:

sbc#show dial-peer voice summary
<output omitted>

200   voip  up   up             map:200            1  syst dns:sip.pstnhub.micr          active     NA

201    voip  up   up             map:200            2  syst dns:sip2.pstnhub.mic          active     NA

202    voip  up   up             map:200            3  syst dns:sip3.pstnhub.mic          active     NA

That’s great if you get that far, but having virtually anything broken will prohibit it. Keep in mind it’s not particularly simple to debug your own outbound OPTIONS pings as “debug ccsip messages” doesn’t show them, and moreover, these are SIP TLS encrypted packets, so a wireshark is of limited help too. The troubleshooting is therefore a little hacky, you basically have to send OPTIONS pings and not only hope you get a response, but if you do get a response, Microsoft will start sending your CUBE OPTIONS pings back at that point in time.

They look like this:

OPTIONS sip:<withheld>.com:5061;transport=tls SIP/2.0
FROM: <sip:sip-du-a-us.pstnhub.microsoft.com:5061>;tag=402f0e5b-097b-4b9c-b74e-e741c66a1d70
TO: <sip:<withheld>.com>
CSEQ: 1 OPTIONS
CALL-ID: 45441787-5ca6-4f06-81f1-7ae844bfb2e0
MAX-FORWARDS: 70
VIA: SIP/2.0/TLS 52.114.148.0:5061;branch=z9hG4bKab92aa27
CONTACT: <sip:sip-du-a-us.pstnhub.microsoft.com:5061>
CONTENT-LENGTH: 0
USER-AGENT: Microsoft.PSTNHub.SIPProxy v.2021.2.16.6 i.USWE2.4
ALLOW: INVITE,ACK,OPTIONS,CANCEL,BYE,NOTIFY

To reiterate that, Microsoft will not send CUBE an OPTIONS ping until CUBE initiates OPTIONS pings.

Callout: Microsoft is very picky about the SBC model, particularly if you want to call Microsoft for help. Don’t expect to use anything that’s not on the supported list. Make sure your packets identify as the appropriate model you’re using.

I’m not going to dive much more into the SIP debugging process. The document is accurate, this all works, but you must have it spot-on. If you don’t have it spot-on, don’t expect debugs to be of much use. Things “just break” with little debug information, more often than not.

The Challenge


This is a good spot to discuss the elephant in the room. While customers are regularly asking us to use MS Teams as a softphone, it’s important to understand that MS Teams is it’s own phone system. It’s not an extension of CUCM. Teams will retain it’s own extensions and dial plan. CUCM has its own extensions and dial plan. You’re effectively tying two phone systems together towards a common goal, rather than adopting MS Teams as a CUCM client. The headache should be fairly obvious: For each user that’s being maintained in CUCM, a duplicate one needs to be created in Teams. For every user removal, a duplicate deletion needs to take place, etc. It’s not particularly simple to maintain, and it’s very easy to get it out of sync.

SIDENOTE: What are others doing?
Most SBC integrations between MS Teams and CUCM rely on the SBC forking inbound calls to both systems simultaneously. The first one to answer the call (be it a desk phone on CUCM, or a soft phone on MS Teams) “gets” the call, and the SBC terminates the call to the system that didn’t pick up. We find this answer inferior because it provides no single point of truth – either phone system may or may not have a log of the call (no CDR), or cradle-to-grave ownership of the call (no option to provide alternate call treatment such as forwarding on no-answer or sending to a call handler). Our goal is instead to have CUCM own the call cradle-to-grave, and treating MS Teams as a softphone solution.

The challenge we faced from here is how to make MS Teams as “softphone-like” as possible. A few items to cover included:

  • Different extensions on different devices is an unnecessary inconvenience calling users.
  • Call Forward No Answer adds a delay when calls are never answered at the first device and limited to no control over where voice messages are left.
  • Requiring users to change a call forward all when working remote is only a workaround – not a good practice that we should ever consider a permanent solution to a business problem.

We want a call to a single extension to ring a device registered to CUCM and MS Teams every time and give the users the choice of which device(s) to utilize and when.

However, when an extension is dialed, CUCM needs to either ring an internal phone, OR route a call outbound – not both.

The Solution


Single Number Reach: Single Number Reach (SNR) is a feature built into CUCM that allows us to “split” a call. When a call rings a CUCM extension with SNR enabled, the call will ring the extension and split to ring a remote destination at the same time. When this occurs, the first device to accept the call takes the call, and a cancellation message is sent to the other destination.

We can adjust timers to control when a call starts and stops ringing the remote destination so we can prevent scenarios such as a dead cell phone instantly answering a call because it goes to their voicemail.

We can force the remote destination to stop ringing before the remote destination issues a ring no answer to send the call to voicemail so we can control what mailbox calls are left on.

We can also use Class of Control to isolate the Route Pattern to MS Teams in its own Partition that is only reachable by the Remote Destination profile. This means that the MS Teams extensions can be the same extension as the user’s CUCM extension so both destinations can ring on the same number without the worry of overlapping numbers.

Advantages over splitting on the SBC

Because the routing/split happens on CUCM at the user lever rather than on the CUBE based on dial-peer we see a few distinct advantages.

  1. Configuration is easier, and can be limited to only users/numbers that need calls split without additional headache.
  2. Feature can be enabled for certain days/times
  3. Feature can be enabled/disabled by end users
  4. Reporting, CDR, and billing remains accurate for any application reporting on calls handled by CUCM.

The configuration

  • First we identify the user that we want to configure Single Number Reach. We access their End User profile and enable mobility on their end user profile.
  • Then we configure a Remote Destination profile for the user. This is what ties together the user information, the DN that rings that triggers the split, and the remote destinations to split the call to.

Key configuration is the User ID of the user, and the Rerouting CSS which is the CSS used to reach the remote destination.

  • Then we select “Add a new DN”, and select the DN and Partition of the line that we want to trigger the split when it rings.
    When done, we go back to the Remote Destination profile configuration, and select “add a New Remote Destination”. Here is where we will configure the MS Teams extension that we want to ring when the users internal extension rings. Then we select “Add a new DN”, and select the DN and Partition of the line that we want to trigger the split when it rings.
  • When done, we go back to the Remote Destination profile configuration, and select “add a New Remote Destination”. Here is where we will configure the MS Teams extension that we want to ring when the users internal extension rings.

Key configuration is the Destination as the DN on MS Teams, and enabling “Enable Unified Mobility features” and “Enable Single Number Reach”. Additionally you can enable “Enable Move to Mobile” which allows a user to press the “Mobility” button when on a call on their internal device and transfer the call to their MS Teams extension.

At the bottom of the screenshot we can see timers where we can adjust when the remote destination should start and stop ringing. These can be adjusted as needed to make sure that calls don’t instantly get answered on the remote end if the device instantly sends a call to voicemail, or we can make the remote destination stop ringing before it sends the call to the remote destinations voicemail (or after if we WANT voicemails left at the remote end).

Below this configuration we have the option to configure time of day routing for Single Number Reach if desired.

After we click save, we will be returned to the Remote Destination configuration page. In the top left, be sure to click “Line Association” next to the DN you want to trigger this Remote Destination, and click save.

And that’s it for the core configuration. At this point, when the desired extension rings, the call will be split and also ring the remote destination that was configured.

Optionally, we can modify the SoftKey template on the user’s phone to give them additional control, for example:

  • If we add the “Mobility” Soft Key when the phone is “Connected”, the user can press the “Mobility” button to send the call to the remote destination without a manual transfer. Note that this only works if the “Enable Move to Mobile” box is checked in the Remote Destination Profile configuration as noted above.
  • If we add the “Mobility” Soft Key when the phone is “OnHook”, the user can press the “Mobility” button to manually enable/disable Single Number reach from their own device. When this is executed from the phone, it toggles the checkbox “Enable Single Number Reach” in the Remote Destination Profile”.

In conclusion: It’s possible to get a “close to a softphone” functionality from MS Teams using it with CUCM. This is accomplished by using Direct Routing between MS Teams and CUCM, and then using SNR on CUCM. However, the setback is having to maintain two separate PBXes simultaneously.

Co-authored by Jeff Kronlage and Nick Finch

Automation With RoomKit

Recently, a customer came to us with an interesting problem with their Room Kits… 

The Challenge

Our client has many Cisco Room Kit installations ranging from large training areas on the Room Kit Pro, to some smaller conference rooms and huddle spaces. Most multi-display video systems have two or more same-sized televisions, generally right next to one another. One screen will show the presentation, the other screen will show the active, remote speaker. The smaller spaces didn’t have enough wall real estate to support two large televisions like the training areas on the Room Kit Pro did. 


Instead, a large primary display along with a smaller secondary display, was chosen:

When the displays are both the same size and right next to one another, it doesn’t usually matter which one is showing the presentation and showing the speaker.

This looked good in visual concept; however, the challenge came in utilizing the larger display for the most prominent media. When there was no presentation to show it made sense to have the speaker on the largest screen, but if the speaker had a presentation to pull up then they needed the larger screen to display the presentation content. 

As illustrated in the photo, the active speaker (screen greyed for customer anonymity) remained on the larger display, and the presentation showed on the smaller display. Shy of moving the HDMI cables by hand every time a presentation started, there was no way to accomplish this on RoomOS.  

Moreover, it was desired that it moved back when a presentation stopped. For example:

Circumstance: Speaker Talking, No presentation

Desired Outcome: Larger TV shows Speaker; Smaller TV Blank

Circumstance: Speaker Talking, with presentation

Desired Outcome: Smaller TV shows Speaker; Larger TV shows presentation

Circumstance: Speaker Talking, was presenting, but stopped presenting

Desired Outcome: Speaker swaps back to larger TV; smaller TV becomes blank

Crossconnect’s challenge was to develop a way to ensure the presentation always ended up on the larger display with minimal user intervention. This presented a variety of complexities since our client needed RoomOS to do this regardless if the presentation was shown via Webex, HDMI input, or Cisco Proximity (https://proximity.cisco.com/). Another added complexity we faced was “How would a Room Kit even know how big a display is?”


How did we accomplish this?

Presented with the complex requirements of our client we knew some custom work was needed. Over the course of two weeks, we developed our own RoomOS script that:

– Tracked the native resolution of the TV as a way to determine TV size

– Ensured if only one input (speaker) was present that it ended up the larger TV

– Triggered logic when a presentation started to ensure the presentation always ended up on the larger TV.

– Triggered logic that when a presentation stopped to ensure that the speaker moved back to the larger TV.

– All of this was accomplished without any user input, it “just works”. In case the user wanted to manually control it, Crossconnect built a button to allow manual display swapping, but this is purely optional

– Was extensible into 3+ display units for future use.

We had an “aha” moment on how to keep the presentation on the larger screen while working on the ‘Call’ and ‘Video Output Connector’ objects within the codecs xAPI interface. We used this in order to determine information about the current monitors as well as to get the information required to asynchronously monitor the device for the following: 

(1) Call status

(2) Call properties (is this a video call?)

(3) Content Sharing

This allowed us to determine which screen received the presentation at any given status change during the call, while monitoring for changes in real time. Initially, we wrote a program that walked the available xAPI endpoints both inside and outside of calls to determine which endpoints needed to be referenced for accuracy. 

The first portion of the program runs when the codec starts up. This initially finds a few key characteristics of the output connectors and determines which one will be the main display monitor.

Here is what that looks like: 

function getResolution(data) {
  for (var set in data) {
    if (data[set].hasOwnProperty("Resolution")) {
      videoOutputDevices.push(data[set]);
      var pushMe = data[set].Resolution.Width + "x" + data[set].Resolution.Height;
      resolutions.push(pushMe);
      screenIDs.push(data[set].id);
    }
  }
  console.log("Video resolutions have been found: " + resolutions.toString().replace(",", ", "));
  
  for (i = 0; i < resolutions.length; i++) {
    if (resolutions[i].split("x")[0] > largestScreen.split("x")[0]) {
      largestScreen = resolutions[i];
    }
    else if (resolutions[i] == largestScreen) {
      console.log("Multiple screens have been found: system will choose the first one found.");
    }
  }
  
  for (i = 0; i < resolutions.length; i++) {
    if (resolutions[i] == largestScreen) {
      screenChoice = screenIDs[i];
      break;
    }
  }
  console.log("Largest screen determined to be screen ID: " + screenChoice + "(" + largestScreen + ")");
  setDisplayUnimportant(screenChoice)
}



After this has been determined it just listens for either:

A) a change in output connectors at which point it would recalculate the metrics mentioned above, or 

B) a call starts, at which point the real meat of the program does its job. 

When a call connects it checks to see if it is a call with video capabilities and if it is, it begins the smart screen presentation features. 

async function getVideoCall() {
  let isVideo = false;
  if (await (xapi.status.get('Call'))) {
    await (xapi.status.get("MediaChannels Call")).then(data => {
      let index, channel = null;
      for (index = 0; index < data[0].Channel.length; index++) {
        channel = data[0].Channel[index];
        if (channel.Direction == 'Incoming' &&
            channel.Type == 'Video' && 
            channel.Video.ChannelRole == 'Main' &&
            channel.Video.Protocol != 'Off') {
            isVideo = true
        }
      }
    });
  }
  return isVideo
}

It will initially set the larger screen to be the main display monitor and sets the smaller screen(s) as the secondary monitor(s). 

As soon as the call listener recognizes that a screen share has been sent locally (through HDMI or through proximity) or remotely (through any SIP dialed device) it switches the screen so that the larger monitor is presenting the screen share and the other screens have the call audience. As soon as the screen share has ended, the larger screen takes over the main portion of the view layout that has been chosen by the end user for the meeting. 

We also gave a manual option to swap the screens with a button if the end user would like to have them be different from the programmatically decided screen. This automatically jumps right back into the smart monitor decisions once the presentation has concluded.  

Once we had the concept working it was time to show our clients. They were absolutely thrilled with how this project turned out. During the proof of concept meeting our client stated their intentions to expand implementation. This is just an example of the way Crossconnect approaches each client’s needs as their own unique situation while creatively attacking the challenge. If you’re facing technology challenges without a clear resolution, then reach out and let our team of engineers help you game plan a solution that works for your company. 

Jeff Kronlage CCIE# 46110 -CEO of Crossconnect