cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Announcements

Welcome to the Cisco Small Business Community

Have a question? Click on a topic board below to get started in the community.

1648
Views
5
Helpful
37
Replies
brantwinter2004
Beginner

Zero touch provisioning from F/W 7.3.7 up to 7.6.1

Hi - I spent an entire day trying to get automate the zero touch provisioning of SPA504G phones from the lowest possible F/W 7.3.7 up to the current 7.6.1.

  • I have DHCP option 66 set to the web server address (example only) "http://192.168.10.10/provisioning" (note it is http as the old phones I don't think have the root CA's compatible with currently issued Cisco CA certs).
  • I factory reset handsets
  • They have an initial profile_rule of /spa$PSN.cfg
    • I place an initial config file, spa504G.cfg, in /provisioning on the web server and confirm the URL works via a browser.
  • The phones boot, I see them get an IP and the DHCP 66 option but then in the NGINX server log I see them try and get spa504G from the root of the webserver, not, /provisioning.
  • I then set up a TFTP server ( I don't want one of these on the network to be honest) and the phones do pick up the config file from provisioning.
  • The next issue, the old firmwares don't seem to support the evaluative logic in upgrade rules, ie:

So I am a bit stuck, I need to ensure I can get phones from the lowest possible F/W revision up to 7.6.1.

I know I have to get them to at least 7.5.2b before 7.6.1.

I want the spa504G.cfg file to come via a http server, not TFTP, and I then want to replace the profile_rule with a https:// URL to the proper config file.

The initial spa504G.cfg file needs to get the phone up to F/W 7.5.2b and then I can do the 7.6.1 F/W upgrade via the final xml profile_rule URL.

I found that in the spa504G.cfg file I could put the downgrade revision limit to 7.6.1 ad then the would prevent a 7.6.1 phone being downgraded to 7.5.2b after a factory reset.

I also thought that if the DHCP 66 option could not set the path as well as the IP to the initial .cfg file I may need to set up a virtual host for the URL name and just put it in the root of the virtual server.

Any help would be appreciated but the requirement is simple to get an SPA504G witg F/W 7.3.7 up to 7.6.1 with my current XML config on it.

On a side note, the 7.6.1 SPC tool would not run under Linux - kept giving a 'segmentation fault'.

1 ACCEPTED SOLUTION

Accepted Solutions

Well. I'm using User-Agent header to identify current version. It have one of two form:

  • Linksys/SPA504G-7.4.3a (1EDF0F4A6C48)(CXT199006KD)
  • Cisco/SPA504G-7.4.3a (1EDF0F4A6C48)(CXT199006KD)

There's model, firmware version, MAC address and serial number. Lets fetch the version number into $_GET["SW"] variable:

$hdr=apache_request_headers();

if (preg_match('/^Linksys\/SPA50.G-/',$hdr["User-Agent"])>0) {
    $_GET["SW"]=strstr(substr($hdr["User-Agent"],16), " ", true);
} else if (preg_match('/^Cisco\/SPA50.G-/',$hdr["User-Agent"])>0) {
    $_GET["SW"]=strstr(substr($hdr["User-Agent"],14), " ", true);
}

Now we know the current version. Now the upgrade logics. Note we are on 7.5.5 now. We are testing 7.6.1. The following fragment *does not* upgrade if phone's current firmware is 7.5.5 or 7.6.1 - otherwise it upgrade to 7.5.5.  Remember the current version is in $_GET["SW"] variable.

if ( $_GET["SW"] != "" && $_GET["SW"] != "7.5.5" && $_GET["SW"] != "7.6.1" ) {
  $upgrade = true;
  // upgrade from FW < 7.5.2b to FW > 7.5.2b require upgrade to 7.5.2b !!
  if (cmp_ver($_GET["SW"], ">=", "7.5.2b")) {
    $upgrade_rule = 'https://$A/Cisco/spa50x-30x-7-5-5.bin?SW='.$_GET["SW"];
  } else {
    $upgrade_rule = 'https://$(A)/Cisco/spa50x-30x-7-5-2b.bin?SW='.$_GET["SW"];
  }
};

Most of work is done. Now you need just to emit appropriate Upgrade_Rule content according the analysis results:

 if ($upgrade) {
echo "<Upgrade_Enable>Yes</Upgrade_Enable>"; } else { echo "<Upgrade_Enable>No</Upgrade_Enable>"; } echo "<Upgrade_Rule>$upgrade_rule</Upgrade_Rule>";

Note the cmp_ver() function used above is my implementation of version comparsion:

function parse_sw_version($v) {

  $str = preg_replace("/^([0-9]+)([a-z]*)\.([0-9]+)([a-z]*)\.([0-9]+)([a-z]*)((\([0-9]*\)(_[0-9]*){0,1}){0,1})$/i", "\$1:\$2:\$3:\$4:\$5:\$6:\$7", $v);
  $ar = explode(':', $str);

  return($ar);
}
function cmp_ver($v1,$op,$v2) {
  $d = "";

  if ($v1 == "*" && $v2 == "*") {
    $d = 0;
  } else if ($v1 == "*" && $v2 != "*") {
    $d = -1;
  } else if ($v1 != "*" && $v2 == "*") {
    $d = -1;
  } else if ($v1 != "*" && $v2 != "*") {
    $ar1 = parse_sw_version($v1);
    $ar2 = parse_sw_version($v2);

    while(true) {
      if ($ar1[0]!=$ar2[0]) { $d=($ar1[0]-$ar2[0]) ; break; }
      if ($ar1[1]!=$ar2[1]) { $d=strcmp($ar1[1],$ar2[2]) ; break; }
      if ($ar1[2]!=$ar2[2]) { $d=($ar1[2]-$ar2[2]) ; break; }
      if ($ar1[3]!=$ar2[3]) { $d=strcmp($ar1[3],$ar2[3]) ; break; }
      if ($ar1[4]!=$ar2[4]) { $d=($ar1[4]-$ar2[4]) ; break; }
      if ($ar1[5]!=$ar2[5]) { $d=strcmp($ar1[5],$ar2[5]) ; break; }
      if ($ar1[6]!=$ar2[6]) { $d=($ar1[6]-$ar2[6]) ; break; }
      $d=0 ; break;
    }
  }

  if ($d < 0 ) {
    $d=-1;
  } else if ($d > 0) {
    $d=1;
  }

  switch($op) {
  case '==': return($d == 0);
  case '!=': return($d != 0);
  case '>=': return($d >= 0);
  case '<=': return($d <= 0);
  case '<': return($d < 0);
  case '>': return($d > 0);
  case '': return($d);
  default: return(false);
  }
}

Hope it help. Ask in doubts. Rate if it help you.

View solution in original post

37 REPLIES 37
brantwinter2004
Beginner

I have again watched Patrick's youtube video (https://www.youtube.com/watch?v=76IMzvfpsx8) and looked at my process again:

SPA504G phone on, say, 7.3.7 (lowest possible F/W).

  • gets a local spa504G.cfg file via local tftp server (I can live with that)
    • simple content, sets resync_periodic to 10s and tells phone to go to URL for XML file
      • the video shows a https URL, will an old 7.3.7 phone have the CA root certs for new Cisco CA issued certs ?
  • phone will then go to http/https server to get .xml profile
    • the profile needs to direct phone to upgrade firmware 
      • I want to upgrade to 7.5.2b first (any higher firmware needs this one first)
        • I want to use this syntax
          • <Upgrade_Rule ua="na">( $SWVER lt 7.5.2b )? http:/xxxxxx/7.5.2b.bin | http://xxxxx/7.6.1.bin</Upgrade_Rule>
        • My lab tests showed (I am certain of it) that the 7.3.7 firmware will not evaluate the upgrade rule syntax (bold above)

So my question is - if the 7.3.7 F/W cannot evaluate the upgrade syntax, how can it ever be zero touch provisioned ?

Thanks guys.

  1. "get spa504G from the root of the webserver, not, /provisioning"
    Use full URL in option 66 - the same you verified in browser to be working, do not remove the file name. 
  2. "old firmwares don't seem to support the evaluative logic in upgrade"
    Old as well as newer firmwares allow numeric comparsion ( lt ) for versions in the form N1.N2.N3 where all three components are numeric. 7b in 7.5.2b is not numeric so numeric operators are not allowed. You are restricted to operators suitable for strings only.
  3. "will an old 7.3.7 phone have the CA root certs for new Cisco CA issued certs"
    Of course no. But you can request certificate signed by CA recognized by both 7.3.7 as well as 7.6.1 firmware. For CA versus phone compatibility table see cross-compatibility list (don't miss the CA name -> EDOS product name mapping on the bottom).

Well, we use zero touch deploying for more than three years (not only for SPA50x but for SPA30x, SPA51x, SPA1xx, SPA2xx, SPA9xx and even PAP2T as well). We have networks deployed in seven countries around. So you may consider simplified description of our configuration valuable. The goal is - devices unknown to us MUST NOT be provisioned (e.g. no name/password leak to unauthorized device). If someone fired reset to factory default, the device should recover to operational state with no mans intervention required. Deployment of new phone must not require personal presence of a IT specialist. So we:

  1. use HTTPS for any provisioning, including the initial one. We use CA certificate recognized by all devices used in particular networks - e.g. Sipura CA if there is a PAP2T or SPA9xx, SB CA if there is a SPA[12]xx. Particular device is identified by client certificate embedded in it.
  2. use DHCP option 160 to deliver full https url to device
  3. provisioning is delivered by PHP script. Such script handle all "firmware version logic" - so we use no (broken) embedded conditionals like ($SWVER ...). Then PHP script consider the upgrade is necessary (and what target version) or not.

Hope it help (rate to lets me know ;-) ).

 

So firmware 7.5.2b is an actual firmware revision, and yes, it is alphanumeric so it will not ever be able to be evaluated? This firmware happens to be the firmware I have to upgrade to first in order to get up to 7.6.1. So without the PHP method you use there is simply no way to zero touch provision a phone (remember my goal is to be able to provision any SPA phone regardless of how old the F/W is)  using Cisco tools ? Are you able to share any more information as to how you are using PHP to determine logic for provisioning and how, from server side, you are influencing firmware revision etc ?

7.52b is non numeric, e.g. only operators suitable for string can ben used - so you can evaluate them to be either equal or non equal to other string, no less/greater than possible

So without the PHP method you use there is simply no way 

You may use $SWVER as part of Upgrade_rule string. So the phone will download firmware file according current firmware version. So you may need no scripting at all.

Also, PHP may be so complex tool for simple task like this one. Even shell script will be enough. We use PHP because "it's here" - we use it for more complex tasks related to phone configuration as well as remote phone dictionary.

Are you able to share any more information as to how you are using PHP to determine logic for provisioning and how, from server side, you are influencing firmware revision etc ?

Yes. but be patient, I will paste relevant fragments of code later (but today)..

Well. I'm using User-Agent header to identify current version. It have one of two form:

  • Linksys/SPA504G-7.4.3a (1EDF0F4A6C48)(CXT199006KD)
  • Cisco/SPA504G-7.4.3a (1EDF0F4A6C48)(CXT199006KD)

There's model, firmware version, MAC address and serial number. Lets fetch the version number into $_GET["SW"] variable:

$hdr=apache_request_headers();

if (preg_match('/^Linksys\/SPA50.G-/',$hdr["User-Agent"])>0) {
    $_GET["SW"]=strstr(substr($hdr["User-Agent"],16), " ", true);
} else if (preg_match('/^Cisco\/SPA50.G-/',$hdr["User-Agent"])>0) {
    $_GET["SW"]=strstr(substr($hdr["User-Agent"],14), " ", true);
}

Now we know the current version. Now the upgrade logics. Note we are on 7.5.5 now. We are testing 7.6.1. The following fragment *does not* upgrade if phone's current firmware is 7.5.5 or 7.6.1 - otherwise it upgrade to 7.5.5.  Remember the current version is in $_GET["SW"] variable.

if ( $_GET["SW"] != "" && $_GET["SW"] != "7.5.5" && $_GET["SW"] != "7.6.1" ) {
  $upgrade = true;
  // upgrade from FW < 7.5.2b to FW > 7.5.2b require upgrade to 7.5.2b !!
  if (cmp_ver($_GET["SW"], ">=", "7.5.2b")) {
    $upgrade_rule = 'https://$A/Cisco/spa50x-30x-7-5-5.bin?SW='.$_GET["SW"];
  } else {
    $upgrade_rule = 'https://$(A)/Cisco/spa50x-30x-7-5-2b.bin?SW='.$_GET["SW"];
  }
};

Most of work is done. Now you need just to emit appropriate Upgrade_Rule content according the analysis results:

 if ($upgrade) {
echo "<Upgrade_Enable>Yes</Upgrade_Enable>"; } else { echo "<Upgrade_Enable>No</Upgrade_Enable>"; } echo "<Upgrade_Rule>$upgrade_rule</Upgrade_Rule>";

Note the cmp_ver() function used above is my implementation of version comparsion:

function parse_sw_version($v) {

  $str = preg_replace("/^([0-9]+)([a-z]*)\.([0-9]+)([a-z]*)\.([0-9]+)([a-z]*)((\([0-9]*\)(_[0-9]*){0,1}){0,1})$/i", "\$1:\$2:\$3:\$4:\$5:\$6:\$7", $v);
  $ar = explode(':', $str);

  return($ar);
}
function cmp_ver($v1,$op,$v2) {
  $d = "";

  if ($v1 == "*" && $v2 == "*") {
    $d = 0;
  } else if ($v1 == "*" && $v2 != "*") {
    $d = -1;
  } else if ($v1 != "*" && $v2 == "*") {
    $d = -1;
  } else if ($v1 != "*" && $v2 != "*") {
    $ar1 = parse_sw_version($v1);
    $ar2 = parse_sw_version($v2);

    while(true) {
      if ($ar1[0]!=$ar2[0]) { $d=($ar1[0]-$ar2[0]) ; break; }
      if ($ar1[1]!=$ar2[1]) { $d=strcmp($ar1[1],$ar2[2]) ; break; }
      if ($ar1[2]!=$ar2[2]) { $d=($ar1[2]-$ar2[2]) ; break; }
      if ($ar1[3]!=$ar2[3]) { $d=strcmp($ar1[3],$ar2[3]) ; break; }
      if ($ar1[4]!=$ar2[4]) { $d=($ar1[4]-$ar2[4]) ; break; }
      if ($ar1[5]!=$ar2[5]) { $d=strcmp($ar1[5],$ar2[5]) ; break; }
      if ($ar1[6]!=$ar2[6]) { $d=($ar1[6]-$ar2[6]) ; break; }
      $d=0 ; break;
    }
  }

  if ($d < 0 ) {
    $d=-1;
  } else if ($d > 0) {
    $d=1;
  }

  switch($op) {
  case '==': return($d == 0);
  case '!=': return($d != 0);
  case '>=': return($d >= 0);
  case '<=': return($d <= 0);
  case '<': return($d < 0);
  case '>': return($d > 0);
  case '': return($d);
  default: return(false);
  }
}

Hope it help. Ask in doubts. Rate if it help you.

Thanks a lot for your help.

Your PHP code, when in the boot process are you looking at the HTTP headers ? And once your code has evaluated, based on the header values, is it writing the following (upgrade disabled in this case):

"<Upgrade_Enable>No</Upgrade_Enable>

to an spa504G.cfg file or is it writing individual spa$MA.xml files ?

I will digest this and see if I can come up with a solution.

For the purpose of firmware upgrade I don't distinguish 'initial provisioning' from 'routine provisioning'.

Our DHCP daemon delivers https://..../Cisco/Provisioning.php via option 160 to the virgin phone. Provisioned phone have the same URL configured in <Profile_Rule...>

Thus phones, either virgin or provisioned already, regardless the firmware version, use this URL only to call for configuration. Yes, HTTP headers are used to identify current firmware version in all cases.

Note the default spa508G.cfg file is never asked by phone connected to our network. Because of DHCP, even virgin phone asks https://..../Cisco/Provisioning.php

is it writing the following ... to an spa504G.cfg file or is it writing individual spa$MA.xml files

No, you missed the idea. I generate no files. The phone asks configuration from .../Cisco/Provisioning.php and such script is sending configuration to them on the fly.

We use no separate site-wide and individual configuration. The Provisioning.php create complete configuration at once. We consider it's faster, so phone can recover quickly.

But of you wish, you can create mixed environment. DHCP and Profile_Rule may call PHP script. Such script will handle firmware upgrade logic and will set Profile_Rule_B and Profile_Rule_C. The Profile_Rule_B will be URL to static file with site-wide configuration while Profile_Rule_C will be static fiel with individual configuration.

But I consider it suboptimal. Even if you have configuration in static file for a reason,. it's faster to order PHP to include it's content to the datas sent - thus phone become provisioned in one step.


Just note - someone may ask why we emit <Upgrade_Enable>No</Upgrade_Enable> if the firmware is the same. It is bandwidth-saving decision. With upgrade disabled the phone will not try to load firmware file to identify the firmware version. It's not necessary - we wish to have full sovereignty to drives upgrade, so we will order phone to upgrade once right time comes.


Just another note - if you wish to distinguish initial provisioning from routine provisioning, set GPP_P variable to something and use it in Profile_Rule (e.g. https://..../Cisco/Provisioning.php?GPP_P=$P). You can test the variable content in the code. If empty - it's initial provisioning. If set to string known to you, you has configured such phone previously.

Great, thanks. That makes it all clear now ! I will get started on a PHP script to generate some test config values.

@danlukes - With your help I have got provisioning via PHP working. I assume you are pulling in phone config data via SQL query to a DB holding all your phone/client info.

This has been a great start - thank you very much for your time and input here.

Glad to hear.

Yes, we have own small simple information system that allow us to configure our phones and client. The database is used in Provisioning.php and it's used to generate text configuration files for our Asterisk as well.

Note that our Provisioning.php supports not only SPA50xG but also other SPA* IP Phones and gateways. There are some trick necessary to support PAP2T (as size of resulting configuration must not exceed 64kB, moreover the embedded client certificate has expired already), there are some firmware bugs related to use of some parameters (for example in Report_Rule), but generally - you can extend the code you have to flexible tool.

Don't bother to ask even in the future.

Hi Dan - I wrote a python based provisioning script (python is easier for string manipulations) based on your code. I can now get a 7.3.7 SPA504G phone up to 7.6.1 and all config applied that is stored in an SQLite DB all with SSL and client auth to the webserver.

This is exactly what I needed ! Thanks again.

See http access log below (it starts iff with me doing a manual downgrade to 7.3.7 to test the process)....

192.168.10.97 - - [16/Mar/2016:01:14:45 +1000] "GET /test/spa5x5-7-3-7.bin HTTP/1.0" 200 3471464 "-" "Cisco/SPA504G-7.6.1 (58BFEA1104E6)(CCQ16380FYH)"

192.168.10.97 - - [16/Mar/2016:01:21:02 +1000] "GET /cisco1.py HTTP/1.0" 200 3600 "-" "Linksys/SPA504G-7.3.7 (58BFEA1104E6)(CCQ16380FYH)"

192.168.10.97 - - [16/Mar/2016:01:22:38 +1000] "GET /spa50x-30x-7-5-2b.bin HTTP/1.0" 200 4160129 "-" "Linksys/SPA504G-7.3.7 (58BFEA1104E6)(CCQ16380FYH)"

192.168.10.97 - - [16/Mar/2016:01:26:13 +1000] "GET /cisco1.py HTTP/1.1" 200 3722 "-" "Cisco/SPA504G-7.5.2b (58BFEA1104E6)(CCQ16380FYH)"

192.168.10.97 - - [16/Mar/2016:01:26:32 +1000] "GET /spa50x-30x-7-6-1.bin HTTP/1.0" 200 4456891 "-" "Cisco/SPA504G-7.5.2b (58BFEA1104E6)(CCQ16380FYH)"

192.168.10.97 - - [16/Mar/2016:01:30:04 +1000] "GET /cisco1.py HTTP/1.1" 200 6628 "-" "Cisco/SPA504G-7.6.1 (58BFEA1104E6)(CCQ16380FYH)"

Glad to hear you got it.

Python is as good as PHP for the purpose of this task. As I mentioned previously -even just plain shell script can handle the provisioning.

Beware of *-XU flavor of firmware. Once installed, they can't be distinguished from non-XU kind of firmwares.

I did see the -XU firmware in the list. How are you handling them ? Do you have an example host header ? I might flash one of these firmwares to an SPA504G and take a look.

-XU and non-XU headers are same, unfortunately.

I wish the brand new unit have no newest firmware we are using, so it will be upgraded.

I never upgrade to -XU firmware thus I wish all -XU units are reverted to be non-XU

May be it is possible to recognize them in spacfg.xml saved, but I didn't tried yet.