Lync-to-Phone for Office 365, First Look

We were recently asked to pilot Lync-to-Phone in Office 365, which is going through a soft launch in the US. It’s not a secret or anything – you can find it at http://pinpoint.microsoft.com/en-us/applications/jajah-voice-for-office-365-12884930736 as a public offering. But, it’s being kept off the radar somewhat as the service ramps up. Because we are a major Office 365 partner, we were asked by Microsoft to go ahead and try it, so we are. This blog post is about our ramp-up experience.

I started with the single provider available, JAJAH, which is from a Spanish telephone company. That got me to here:

clip_image001

clip_image002

I accidentally picked the wrong plan, but I was able to close, sign in, and was prompted to reselect my plan and move forward.

clip_image003

clip_image004

Or at least I tried to move forward:
Something went wrong

Did a customer support call – took six tries (I am not exaggerating) for them to get my e-mail address right, even with phonetic spelling, which was a rough start, but after about three minutes they had it. Their answer? “Try a different browser with cleared cookies and browser history and try again.” Well, I had been using Firefox because IE 10 RP wasn’t making it past the first screen, but I went over to an IE10 Porn Mode In Private session and tried again. Yet again, I could not take any button presses to register, and they had set their site to not allow compatibility mode. So, I used F12 to force the browser to IE10 Compatibility Mode:

Compatibility Mode

And then tried again to log in. This time, the sign-in button worked, but the login failed. Great. So I tried to register again from the beginning. That time it said I was already registered. More great… So I did a “forgot password” on the sign-in page (even though I know I had it right because I had signed in with Firefox using the same password pasted from the same entry in my password management application). This caused a generated password to be sent by e-mail, in theory at least.

I never got the e-mail, and the original login still worked in Firefox. So then it’s a question of what browser I’m supposed to use. Firefox doesn’t work, IE 10 doesn’t work… so now I needed to install another browser. I guess it’s time to install Chrome… so 20 minutes lost…

Nope… no luck there either. Of course doing anything in Chrome that’s not on a Google site or using WebKit custom proprietary stuff is a coin toss but still, WTF? Then I remembered that I could look up the password that continued to work in Firefox by looking at its stored password list… and it wasn’t the password I set; it was truncated. No idea what was up there – a bug in the registration process I guess… only the first ten characters were taken. Now that I knew that, I could try to sign in using IE again (again using F12 to set compatibility mode)… and that failed again.

So I went back to Chrome again… logged in successfully. And that failed to add the location again. So now I’m back to calling customer service again.

This time, they got my e-mail on only the second try, without use of a phonetic alphabet.. Oops, no, third try… that failed again, so we went to phone number. That seemed to work – I was put on hold again while they looked at the account. It might be part of this goes back to bailing out partway through as described above, but decent QA should have caught this. This offering has been in development for months – some basic testing would have been useful I think… anyway, that led to a 24-48 hour delay while the case was escalated to an engineer… so now we’re in a holding pattern…

… insert multiple days of hold music …

OK, so it was some kind of error on the JAJAH side, but it’s been resolved. How do I know? Because “Yahoo! Voice by JAJAH Customer Service” wrote me and told me it was.  (Aside: I am forced to assume that this is key insider trading information about Microsoft buying Yahoo! now that Jerry Yang turned down a jillion dollars to get the company worth the $3.12 it’s worth today [a steal at half the price!].) So let’s go back in, remembering that Internet Explorer 10 doesn’t work. I went to the site again, sign in again, and again re-select my calling plan and initial account information. This time, adding a location works:

Adding a Location

I just did one location for now – it will make sense to add others later but this is good enough to get started. Clicking Continuegets me to phone line selection:

clip_image009

So more “good” news here – there’s only two area codes available for all of Ohio (luckily the one we probably want – 216 – is here), and there’s at least one grammar error (“request addition phone numbers“) on the page. Okay, so let’s take 216 and see what we get:

Line quantity

We get forty-nine lines available. I don’t know if that’s meant to be for 216 or for our account alone. For testing I’m going to take two but I definitely don’t like such a small number showing. You can actually add numbers to multiple locations as part of the order all at once, which is nice – each time you “ADD TO ORDER” it shows up on the right-hand order summary:

Order summary

Moving on, it’s payment time!

Payment Info

The complaints about blank fields weren’t there when I started, it’s an artifact of my forgetting to screenshot before entering something. So no bug there. I put in my work-issued credit card that I’ve already had cleared to use for this, and get a confirmation:

Almost there!

Still not sure about the $13.99 to $18.99 range here, but after all this, if I didn’t Submit then that would be silly, right? So time to click. And …. It failed again:

Another error? Inconceivable!

Great, another call to customer service so I can spend twenty minutes spelling out my e-mail address… this time I told them to use cell number to look it up, and they fail, so we spend more time with going through my e-mail address, reading it multiple times, and convincing them it’s not a browser issue (everything with them seems to start with assuming you don’t know how to use a web browser), and then finally opening another escalation, which means another 24-48 hours. I wish I was kidding.

Okay, so about 36 hours later, it was “fixed,” so time to try again. I again had to walk through the whole process. This time the final screen showed different results, which suggested it was going to work:

Final screen again

And yay, it did:

It's alive!

I also received a line confirmation e-mail:
Confirmation e-mail

So time to activate! This one at least didn’t reference Yahoo! in the Fromaddress, so there’s that. Moving on, it’s activation time:

Activation

So time to activate my line and assign it to me, starting with the first of the two Activate links (nice use of a little DHTML here):

Activation details

I put in my e-mail address, left the location alone for now, and entered my name, then hit Save. This changed the icon in the front to indicate the line was active:

Activated!

I also received a confirmation e-mail on my location change with some slightly off sentence structure:

Location confirmation

Now I had to assign it to myself. There are instructions on the JAJAH site so they must work, right? I started signing in to the Office 365 Portal (I have administrative access). I located my User information, went to Details, and uh oh, we’re syncing our user information, so my office number wasn’t able to be changed. So the instructions are wrong in our case and lots of other cases. That’s fine, I can go to our on-premises Active Directory and set the number, then wait, which is what I did.

The office phone number replicated, so now it was time to set the provider in Lync Online. I went to the Admin Portal and selected Lync, Manageto get to the Lync Online Control Panel. I located my user account on the Users tab, and clicked my name. And then I… did nothing, because our Office 365 plan is E1 and you must have Lync Plan 3 to do Lync to Phone. So I enabled the 30-day trial of Lync Online (Plan 3) and tried again. Provisioning the plan required a very short wait (under one minute), then I was able to go into Licenses and assign myself one of the trial licenses. Then, I went back into the Lync Online Control Panel, and went into my user properties. This happened:

Unknown application error

Yay! Yet again a step breaking. This is getting fun.

So I waited about 21 hours and tried again, and this looks better:

Selecting a provider for the user

OK, so what difference did this make? Well, the Lync 2013 Preview Release client has all kinds of weird bugs, some of which I think impact this work, so I’m going to do it in Lync 2010. First noticeable change is that I have PBX functions showing in the client now:

PBX Functions

The location I was forced to add and had so much trouble with before didn’t show up anywhere, but whatever. It would be wrong as I’m typing this anyway as I’m in the United Club in Seattle, which is very far away from the location they have on file.

So I attempted to place a call, and it was successful! But could I receive a call?

Hellllooooo?

Yes, yes I could. This is exciting, we have phone calls! So of course I could leave an Exchange voice mail, right? Well, no, actually, I just get a busy signal when I decline. So that is awful. Time for more delay as I wait on an SR with Microsoft. But that’s going to take some time, and I think it’s too much time at this point, so I’m posting, and I’ll do another post when it’s resolved.

Michael C. Bazarewsky
Principal Consultant, Server and Security

Using UDP-SIP with Exchange UM and Lync 2010

Attachment: https://bennettadelson.wordpress.com/2012/06/04/using-udp-sip-with-exchange-um-and-lync-2010/kamailio-cfg/ (remember to change extension)

Attachment: asterisk.tar.gz (remember to change extension)

I am working on and off with a client that is deploying Exchange 2010 Unified Messaging and Lync 2010 in their environment. They want to use Exchange UM with a hosted SIP-based VoIP system from a provider that I will refer to as “PhoneCo” for the sake of discussion. Furthermore, they want their Lync environment to work with the Exchange voicemail, and by the way, think it would be nice if they could experiment with Enterprise Voice functionality. Luckily, PhoneCo offers SIP trunks, and will trunk from the hosted VoIP environment to Exchange UM. So all is good, right?

The Problem Statement

Ha ha, of course I am joking. Because although Microsoft talks SIP, and PhoneCo talks SIP, we hit upon a long-standing issue. Microsoft refuses to support UDP SIP (they have their reasons, I won’t debate the point here) while PhoneCo refuses to support TCP SIP. Thus, we have an impasse.

Solution Overview

The official, standard answer to this is to use a Session Border Controller (SBC), which is essentially a SIP middleman box that can do UDP on one end and TCP on the other. A typical SBC also includes firewalling intelligence to prevent denial-of-service and other such nasty behavior. As a result, they generally start at thousands and quickly get into tens of thousands of dollars. In this customer’s case, the SIP trunk is going to be over a private MPLS connection directly between the hosted PBX and the on-premises Microsoft tools, so the customer didn’t want to pay for a lot of security they didn’t need just to deal with this issue.

The customer found a commercial product named Brekeke SIP Server that appears to be $500 to start. This is nice in that (1) it is commercial and (2) it can run on Windows, although it is Java-based so it’s a little messy and gives you one more thing to deal with patching every day or two.

We wanted to see if there was an open-source way to solve this problem. We found a way, and this post documented what we came up with. I have replicated the scenario in a lab, and have since actually simplified things a bit. I have also corrected something we had done to work around an Asterisk “bug” (in quotes because the bug states it’s not really an Asterisk bug) that came up while we were simulating the PhoneCo setup.

So first, here’s the list of VMs that are in the UC Lab:

Hostname IP Description
dc.uclab.local 172.30.1.10 Domain Controller
exchange.uclab.local 172.30.1.12 Exchange 2010
freepbx.uclab.local 172.30.1.11 PhoneCo stand-in
lync.uclab.local 172.30.1.13 Lync 2010
siprouter.uclab.local 172.30.1.14 SIP middleman
tmg.uclab.local 172.30.1.1 TMG 2010
internalclient.uclab.local 172.30.1.100 Test Lync/SIP client

The PhoneCo stand-in is a FreePBX installation using the FreePBX Linux distribution. I am not going to go into details on installing that into a VM because there are plenty of guides on getting that to work. For the purposes of this post I’m going to pretend Asterisk can’t do TCP SIP because that’s what we are looking at with PhoneCo. This also means ignoring all the online info about getting Asterisk to talk to Lync and Exchange using TCP SIP. (Note: Some of these guides assuming port 5065 for talking to Exchange, which is a partial solution. I’ll get into why that’s wrong later on.)

The SIP middleman – SIP router – is a Linux-based CentOS machine running the Kamailio open-source SIP router package. Kamailio is a mature, solid package that is quite amazing in some of what it can do, but I’m ignoring about 99% of it, I think. We may end up needing some of the NAT support eventually at the client, which I’m not getting into here and don’t need for the lab, but otherwise a lot of functionality is actually not in play here.

Preparing the CentOS Machine

So let’s get to it.

  1. I began with a basic minimal CentOS 6.2 installation. Note that I’ve had repeated issues with the Hyper-V Integration Components on this OS so far, so I didn’t bother with them – for a lab it’s not critical. For production you’d care a lot more – the customer uses VMware so this particular issue did not come up.
  2. Next, I logged in as root via SSH (PuTTY is your friend here) and accepted the key when prompted:
    image
    image
  3. I ran yum updateto get all of the current updates for the OS, and rebooted to get the updated kernel loaded.
  4. Using vi, I created /etc/yum.repos.d/kamailio.repowith:
    [kamailio]
    name=Kamailio
    baseurl=http://download.opensuse.org/repositories/home:/kamailio:/telephony/CentOS_CentOS-6/
    enabled=1
    gpgcheck=0

    This looks like this:

    clip_image001

  5. I confirmed that the new repository was visible with yum repolist:clip_image002
  6. I then confirmed that there was a package I could install in that repository with yum list kamailio:
    clip_image003
  7. After confirming the package, I installed it with yum install kamailio:
    clip_image004

    clip_image005
  8. So now I need to configure the beast. Kamailio comes with a very long sample configuration file. Most of it is noise for my use. I tried to trim it down as safely as possible, as well as better fit what I wanted. So using the following commands I saved the shipped file:
    cd /etc/kamailio
    mv kamailio.cfg kamailio.cfg.original
    vi /etc/kamailio.cfg

    And then made mine, which I will explain later after finishing the build instructions:

    #!KAMAILIO
    
    # Remote Hosts
    #!subst "/SIP_UDP_HOST/172.30.1.11/"
    #!subst "/EXCHANGE_UM/172.30.1.12/"
    #!subst "/LYNC_MEDIATION/172.30.1.13/"
    
    listen=172.30.1.14:5060
    listen=172.30.1.14:5065
    listen=172.30.1.14:5067
    
    ####### Global Parameters #########
    
    memdbg=5
    memlog=5
    
    debug=2
    
    log_facility=LOG_LOCAL0
    
    fork=yes
    children=4
    
    disable_tcp=no
    
    auto_aliases=no
    
    /* uncomment and configure the following line if you want Kamailio to
       bind on a specific interface/port/proto (default bind on all available) */
    #listen=udp:10.0.0.10:5060
    
    # life time of TCP connection when there is no traffic
    # - a bit higher than registration expires to cope with UA behind NAT
    tcp_connection_lifetime=3605
    
    ####### Modules Section ########
    
    mpath="/usr/lib/kamailio/modules_k/:/usr/lib/kamailio/modules/"
    
    loadmodule "kex.so"
    loadmodule "tm.so"
    loadmodule "tmx.so"
    loadmodule "sl.so"
    loadmodule "pv.so"
    loadmodule "maxfwd.so"
    loadmodule "usrloc.so"
    loadmodule "textops.so"
    loadmodule "siputils.so"
    loadmodule "xlog.so"
    loadmodule "sanity.so"
    loadmodule "ctl.so"
    loadmodule "cfg_rpc.so"
    loadmodule "mi_rpc.so"
    
    # ----- tm params -----
    # auto-discard branches from previous serial forking leg
    modparam("tm", "failure_reply_mode", 3)
    # default retransmission timeout: 30sec
    modparam("tm", "fr_timer", 30000)
    # default invite retransmission timeout after 1xx: 120sec
    modparam("tm", "fr_inv_timer", 120000)
    
    server_header="Server: PhoneCo Intransigence Coping Solution (PICS) 2.0";
    
    ####### Routing Logic ########
    route {
            if(is_method("OPTIONS")) {
                    xlog("L_INFO","OPTIONS from $si");
                    sl_send_reply("200", "Yes, Microsoft, I am alive");
                    exit();
            }
    
            xlog("L_INFO", "*** M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci");
    
            # Route Exchange extensions
            if((to_uri=~"sip:5992") || (to_uri=~"sip:5999")) {
                    xlog("L_NOTICE", "EXCHANGE UM call, $proto port $op, $ru, $fU");
                    t_on_reply("1");
    
                    # https://issues.asterisk.org/jira/browse/ASTERISK-16862
                    # http://imaucblog.com/archive/2009/10/03/part-1-how-to-integrate-exchange-2010-or-2007-with-trixbox-2-8/
                    replace("Diversion: <sip:5999@SIP_UDP_HOST>;reason=unconditional","MCB-Stripped-Header: Diversion");
    
                    switch($op) {
                            case 5060:
                                    xlog("L_NOTICE", "Redirecting to TCP 5060");
                                    t_relay_to("tcp:EXCHANGE_UM:5060");
                                    exit();
                                    break;
                            case 5065:
                                    xlog("L_NOTICE", "Redirecting to TCP 5065");
                                    t_relay_to("tcp:EXCHANGE_UM:5065");
                                    exit();
                                    break;
                            case 5067:
                                    xlog("L_NOTICE", "Redirecting to TCP 5067");
                                    t_relay_to("tcp:EXCHANGE_UM:5067");
                                    exit();
                                    break;
                    }
            }
    
            # Route Lync extensions
            if(to_uri=~"sip:5...") {
                    replace("To: <sip:", "To: <sip:+");
                    xlog("L_NOTICE", "LYNC call to $tu");
                    t_relay_to("tcp:LYNC_MEDIATION:5068");
                    exit();
            }
    
            # Route the rest to Asterisk
            xlog("L_NOTICE", "Asterisk call to $tu");
            forward_udp("SIP_UDP_HOST", 5060);
    }
    
    onreply_route[1] {
            xlog("L_NOTICE", "Handling reply from Exchange relay, status $rs");
            switch($rs) {
                    case 302:
                            xlog("L_NOTICE", "Saw 302 Redirect response, checking details...");
                            if(search(";transport=Tcp")) {
                                    xlog("L_NOTICE", "Saw TCP redirection, changing redirection to UDP");
                                    replace(";transport=Tcp", ";transport=Udp");
                            } else {
                                    xlog("L_NOTICE", "302 was not matched (!)");
                            }
                            exit();
                            break;
                    case 100:
                            xlog("L_NOTICE", "Saw 100, leaving alone...");
                            exit();
                            break;
            }
    
    }

     

  9. I stared the daemon (read: service) with /etc/rc.d/init.d/kamailio start and confirmed that it started  with /etc/rc.d/init.d/kamailio status:clip_image001
  10. I confirmed it was listening (netstat –an | grep 506):clip_image002
  11. I then opened up the firewall to allow those ports in (okay, thats a lie – I floundered a bit before remembering I had to do this) by editing /etc/sysconfig/iptables and adding after the --dport 22line:
    		-A INPUT -p tcp -m state --state NEW -m tcp --dport 5060 -j ACCEPT
    		-A INPUT -p tcp -m state --state NEW -m tcp --dport 5065 -j ACCEPT
    		-A INPUT -p tcp -m state --state NEW -m tcp --dport 5067 -j ACCEPT
    		-A INPUT -p udp -m state --state NEW -m udp --dport 5060 -j ACCEPT
    		-A INPUT -p udp -m state --state NEW -m udp --dport 5065 -j ACCEPT
    		-A INPUT -p udp -m state --state NEW -m udp --dport 5067 -j ACCEPT

    This looks like this when it’s done:
    image

  12. I then made this kick in by restarting the firewall with /etc/rc.d/init.d/iptables restart.
  13. I next added system logger support for the configured log source by editing /etc/rsyslog.confand adding:
    local0.*                                                 /var/log/kamailio.log

    image

  14. I then made this kick in by reloading the logger configuration with /etc/rc.d/init.d/syslog reload.
    image
  15. I don’t want this log to grow uncontrollably so I configured the logrotate daemon to make a new log every day and save seven of them by creating /etc/logrotate.d/kamailiowith:
    /var/log/kamailio.log {
    	rotate 7
    	missingok
    	daily
    }

    image

Preparing Exchange 2010 and Lync 2010

This is normal Exchange and Lync SIP configuration so I’m not going to get into great detail here. The following are the key points:

  • Make sure Lync has a TCP listener on port 5068 on the mediation server of your choice. There’s no high availability here so pick one and go. As quick hints of where this is done in Topology Builder:
    clip_image001[7]
    clip_image002[8]
    After publishing and running Bootstrapper (Lync Setup) on the Mediation Server as instructed by Topology Builder I ran into (what I consider to be) a bug in Lync shown via the event log – there were LS Mediation Server messages 25075 and 25031 indicating no TCP port is enabled, then that the TCP port was requested but ignored. Restarting the Mediation Server service sorted it out. The Kamailio log will show this working (e. g. tail /var/log/kamailio.log):
    image
  • For Exchange, make sure you have TCP enabled on the UM server (requires a service restart to kick in) and that you have an appropriate IP gateway and unsecured telephone extension dial plan configured against that gateway:
    clip_image001[9]
    clip_image002[10]

And that’s it!

So What Does the Configuration Mean?

OK, so what the heck does the configuration I gave you above mean?  Let’s go through it:

#!KAMAILIO

This is a signature for the configuration file.

# Remote Hosts

#!subst "/SIP_UDP_HOST/172.30.1.11/"
#!subst "/EXCHANGE_UM/172.30.1.12/"
#!subst "/LYNC_MEDIATION/172.30.1.13/" 

listen=172.30.1.14:5060
listen=172.30.1.14:5065
listen=172.30.1.14:5067

This is the super important customization part. The three subst lines replace all references to those text strings with the appropriate IP addresses, while the listen lines allow the router to accept traffic on its IP on three ports – 5060, 5065, and 5067. The latter two are because Exchange – for reasons known to Microsoft but not me – takes UM connections on port 5060 but then redirects them to 5065 or 5067. Remember how above I said that some sites use 5065 and that’s wrong?  That’s because they are assuming all redirects are to 5065, but Exchange might want 5067.

Anyway, the next lines are some configuration stuff that is from the default that I left alone mainly because either the settings were fine (e. g. the syslog facility used) or because I didn’t know the implications in changing them (e. g. the children process count); there’s also the enabling of TCP (normally disabled):

####### Global Parameters ######### 
memdbg=5

memlog=5 
debug=2
log_facility=LOG_LOCAL0 
fork=yes

children=4 
disable_tcp=no 
auto_aliases=no 

# life time of TCP connection when there is no traffic
# - a bit higher than registration expires to cope with UA behind NAT
tcp_connection_lifetime=3605

Next are the modules that I am loading. I know I need some of these for sure – there are others I don’t know about so I left well-enough alone and kept them there:

####### Modules Section ######## 
mpath="/usr/lib/kamailio/modules_k/:/usr/lib/kamailio/modules/" 
loadmodule "kex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "mi_rpc.so" 

# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)

The next line sets a server header seen in the SIP headers. It is a fun way to point out that PhoneCo was annoying me as well as to hide the actual software being used:

server_header="Server: PhoneCo Intransigence Coping Solution (PICS) 2.0"

Now comes the real meat. It starts the routing logic for incoming SIP calls looking for the OPTIONS call that Lync and Exchange make every nanosecond (approximately) to check to see if their SIP peers are alive. Hence the status text – the code is all that really matters:

####### Routing Logic ########

route {
        if(is_method("OPTIONS")) {
                xlog("L_INFO","OPTIONS from $si");
                sl_send_reply("200", "Yes, Microsoft, I am alive");
                exit();
        }

The next line just acts as a debugging log showing what came in:

        xlog("L_INFO", "*** M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci");

The dollar-sign pseudo-variables are documented here, should you care: http://www.kamailio.org/wiki/cookbooks/3.2.x/pseudovariables

Anyway, moving on, we have the Exchange routing. Looking at this now, I probably want the two extensions (one for the auto-attendant and one for subscriber access) to be substituted variables, but that will be 2.1 I guess:

# Route Exchange extensions
        if((to_uri=~"sip:5992") || (to_uri=~"sip:5999")) {
                xlog("L_NOTICE", "EXCHANGE UM call, $proto port $op, $ru, $fU");
                t_on_reply("1");

This basically says “if a SIP call is made to extension 5992 or extension 5999, then do this…” and starts by indicating that we are going to do a transactional SIP redirect that, when we see a reply, should go to reply handler “1“, which will come later. After that, we have:

        # https://issues.asterisk.org/jira/browse/ASTERISK-16862
        # http://imaucblog.com/archive/2009/10/03/part-1-how-to-integrate-exchange-2010-or-2007-with-trixbox-2-8/
        replace("Diversion: <sip:5999@SIP_UDP_HOST>;reason=unconditional","MCB-Stripped-Header: Diversion");

Why is this here? Basically, Asterisk does something we don’t want it to do on the Exchange redirect – adds an extra SIP Diversion header – and we want that extra header to go away. I need to replace it with something though, so I just made up a vendor header and used that. This is safe as SIP agents – like HTTP server and clients – ignore headers that they don’t know. Next, we take the UDP session and do a transactional redirect to TCP:

        switch($op) {
                case 5060:
                        xlog("L_NOTICE", "Redirecting to TCP 5060");
                        t_relay_to("tcp:EXCHANGE_UM:5060");
                        exit();
                        break;
                case 5065:
                        xlog("L_NOTICE", "Redirecting to TCP 5065");
                        t_relay_to("tcp:EXCHANGE_UM:5065");
                        exit();
                        break;
                case 5067:
                        xlog("L_NOTICE", "Redirecting to TCP 5067");
                        t_relay_to("tcp:EXCHANGE_UM:5067");
                        exit();
                        break;
                }
        }

I couldn’t come up with a “smart” way to do this better; this is a little wordy but it is clear what is happening. I next route the Lync calls (adding the E.164 “+” sign along the way) based on extension pattern (all other 5xxx extensions besides the two special case ones above), with all others going to the Asterisk side:

        # Route Lync extensions
        if(to_uri=~"sip:5...") {
                replace("To: <sip:", "To: <sip:+");
                xlog("L_NOTICE", "LYNC call to $tu");
                t_relay_to("tcp:LYNC_MEDIATION:5068");
                exit();
        }

        # Route the rest to Asterisk
        xlog("L_NOTICE", "Asterisk call to $tu");
        forward_udp("SIP_UDP_HOST", 5060);
}

Notice that I do forward_udpinstead of t_relay_to because I don’t care about maintaining transactional state in the case of going back to Asterisk, so there’s no reason to waste resources on it. I just tell Kamailio to throw it over the wall and forget about it.

Finally, I handle the reply from Exchange. This is why I made the Exchange piece transactional:

onreply_route[1] {
        xlog("L_NOTICE", "Handling reply from Exchange relay, status $rs");
        switch($rs) {
                case 302:
                        xlog("L_NOTICE", "Saw 302 Redirect response, checking details...");
                        if(search(";transport=Tcp")) {
                                xlog("L_NOTICE", "Saw TCP redirection, changing redirection to UDP");
                                replace(";transport=Tcp", ";transport=Udp");
                        } else {
                                xlog("L_NOTICE", "302 was not matched (!)");
                        }
                        exit();
                        break;
                case 100:
                        xlog("L_NOTICE", "Saw 100, leaving alone...");
                        exit();
                        break;
        }

Notice if I get a redirect from Exchange (which I will for port 5060) I change that from a Tcp redirect to a Udp redirect, then send it on its way.

So, this is what is in the lab right now. I think this works – until PhoneCo gets the line in place we won’t know 100% but I think this is close if it isn’t completely right. We’ll see.

Hope this helps you in your integration scenarios!

— Michael C. Bazarewsky
Principal Consultant, Windows Server and Security