Shoretel, LLDP and DHCP headache

During the process of migrating to new DHCP servers my colleague noticed a lot of inactive leases in the DHCP scope for our data subnets. After cross referencing MAC addresses it became apparent that the leases belonged to our Shoretel IP phones. All of the phones also have active leases in the VoIP DHCP Scope.

First of all we took a look at the DHCP scope options. Option 156 was enabled on both scopes, containing the following.


We decided to break out trusty Wireshark to try and figure out what was going on. From here we could see what was going on. When the phones were booting up they were doing the following:

  1. Trying LLDP
  2. Requesting DHCP lease from the untagged vlan on the switchport they were plugged into (data subnet)
  3. Retrieving option 156 from the data scope and reconfiguring themselves to tag voice traffic with vlanid.
  4. Requesting DHCP lease from the tagged vlan on the switchport they were plugged into (voice vlan)
  5. Continuing normal boot.

Step 2 is where the phones were getting the lease in the data subnet.

We decided to try lldp-med on the Cisco 2960S switches that we use for access.

This is probably a good time to mention that all of this was tested thoroughly in a controlled environment before it was rolled out to end users. We are not responsible if you break your phone system after reading about our investigations here.

conf terminal
interface range Gi1/0/1-48
switchport mode access
switchport access vlan 100
switchport voice vlan 200

I won’t go into detail about the QoS config we are running. It is there though, just not shown here.

Next we removed option 156 from the data scope and changed the string in option 156 on the voice scope to the following.


After rebooting the phones they did the following.

  1. Negotiated LLDP with the switch.
  2. Requested DHCP lease from the tagged vlan on the switchport they were plugged into (voice vlan)
  3. Retrieved option 156 from the voice scope and reconfigured themselves NOT to tag voice traffic.
  4. Requested DHCP lease from the untagged vlan on the switchport they were plugged into (data subnet)
  5. Used a cached config file and continued booting.

Strange. We did some more tinkering and found that if we added the layer2tagging=1,vlanid=200 options back to the option 156 string and rebooted the phone they stayed in the correct vlan. From this we took an educated guess that the phones were assuming the defaults of layer2tagging=0,vlanid=0 if the option were not specified in the option 156 string.

Next we removed option 156 from the voice scope and added option 66, which it a Boot Server Address, and set it to the ftpservers address from the option 156 string, 192.168.***.***.

We rebooted the phones and they did the following.

  1. Negotiated LLDP with the switch.
  2. Requested DHCP lease from the tagged vlan on the switchport they were plugged into (voice vlan)
  3. Retrieved option 66 from the voice scope.
  4. Contacted the FTP server.
  5. Continued booting normally.

Success! Or so we thought. The country and language of the phones had both reverted to 1 and 1, meaning the dial tone was different, although the language was still English.

To get around this we changed the config files for the phones on the FTP server. The files were stored in c:inetpubftproot on the Shoreware Director server. The file names are shore_model.txt, where model is referred to on the white label on the back of every model. The original config file looked like this.

There is an updated method to accomplish this, whcih you can find in my newwer post ShoreTel LLDP Followup.


We changed it to the following.


After a quick reboot the phones were back to their normal selves.


Proxy Automatic Configuration Files

So I finally decided to start blogging, after countless mental arguments with myself about what I will blog about, and whether or not I have time for this shit… Turns out I do have time, and what better topic could I select then that grey area of networks that is PAC, or WPAD scripts?

There has been a lot of hype over the years surrounding the security of PAC scripts, leaving gaping back doors into networks. Hackers can do all sorts of clever things involving DHCP, DNS and WINS spoofing to silently re-route web traffic through a malicious proxy server, exposing valuable credentials, such as network passwords or even financial details.

Although a lot of things can be done client side to prevent such attacks, certain operating systems still remain vulnerable to these scenarios, and studies show that the most effective way to protect your network from these attacks, is to implement them yourself.

So what is a PAC file, and why aren’t they more widely used?

A PAC file is a Proxy Automatic Configuration File. It is used on a network that requires connection to the Internet via a web proxy to publish the settings of the proxy server. The main reason for the lack of uptake, is the complexity of implementation and administration of these files. Although there are more complex systems out there, the advantages of a PAC file are minute when measured up against the administration required to keep them optimized.

So today I decided to do something about the administrative nightmare, and attempt to bring some order back into the good old pac file. I will share my techniques with you, and hopefully help the next average Joe to attempt this in some way.

I by no means believe this to the most secure way to implement PAC files on your network, but I believe it to be an easier administrative task managing exceptions and routes for the PAC file. Bare with me, this is the first time I have ventured into blogging so I may well be useless, but we will see.

So here’s what I did.

First thing that we need to do is configure our network to serve up the PAC or WPAD file. I chose a WPAD file, for simplicity, but both files work in the same way. We will need to configure DNS, DHCP and IIS to accomplish our goal, so we will start with DNS.

A lot of clients will automatically look for a wpad DNS entry on the domain they are authenticated to. So for instance, if your fully qualified computer name was, your system would search for If it doesn’t find the entry, it will drop a level and search for, and then It will never search outside of the highest level search domain on your network.

To create our DNS entry, we need to do the following.

  1. Open DNS Management Console from Start > Programs > Administrative Tools >DNS Management.
  2. Drill into the look-up zone you wish to create the entry, and click New Host (A).
  3. In the “Name” box, type “wpad” in lower case without the quotes.
  4. In the “Address” box type the ip address of your web server.
  5. Save the entry and we are done in DNS.

Now we will move onto DHCP. Although you technically only need one form of auto-discovery, in the interest of security we will setup both DNS and DHCP. You could also setup WINS, but we don’t use wins in our production environment, so the configuration of WINS was beyond the scope of this exercise.

To start with, we need to configure the server to allow the option.

  1. Open DHCP Management from Start > Programs > Administrative Tools.
  2. In the left-hand pane, Right-Click the DHCP server and click Set Predefined Options.
  3. Click Add.
  4. In the “Name” field type WPAD.
  5. In the “Code” field type 252.
  6. In the “Data Type” field, select String, followed by OK.
  7. In the “String” field, type the URL of your PAC file. It should look something like this
  8. Right-Click Server Options and click Configure Options.
  9. Ensure that option 252 is available.

Now we configure each DHCP Scope that we want the wpad file to be sent to.

  1. In DHCP Management, Right-Click on Scope Options, and select Configure Options.
  2. Click on Advanced, and then from Vendor Class select Standard Options.
  3. From Available Options, select 252 Proxy AutoDiscovery and click OK.

That should be out DHCP configuration complete.

Now we need to configure IIS to process PHP scripts. I cheated slightly with the installation of PHP, and used microsofts new Web Application Installation service to install PHP, which I found here

In your document root (C:inetpubwwwroot by default), create a new file called wpad.dat. By default, the webserver will not server the file, as it does not know what it is or what it does. We need to add a MIME type to IIS.

  1. Open Internet Information Services Management from Start > Programs > Administrative Tools.
  2. Drill down to Default Website in the left-hand pane.
  3. From the right-hand pane select MIME Types.
  4. Right Click the white space in the right-hand pane and select add MIME Type.
  5. In the “File Name Extension” box type .dat.
  6. In the “MIME Type” box, type "application/x-ns-proxy-autoconfig" without quotes.
  7. Click OK.
  8. Restart your server.

Our wpad file will now work once the client is set to “Automatically Configure Proxy Settings”. But that was not the goal of this whole excercise. In order to dynamically generate the wpad file every time it is requested, the server needs to process the file on request in PHP. To accomplish this, we need to tell IIS that .dat files are to be sent to the PHP engine we installed earlier.

  1. In Default Website, within IIS Management, Select Handler Mappings.
  2. Right-click on the white space in the right-hand pane, and select “New Module Mapping”.
  3. In “Request Path” type wpad.dat.
  4. Select “FastCGIModule” from the “Module” list.
  5. In the “Executable” field type “C:Program FilesPHPphp-cgi.exe” including the quotes.
  6. In the “Name” filed, type WPAD.
  7. Click OK.
  8. Restart IIS.

Now the wpad.dat file will be processed in php every time it is requested. We decided that we would like to hold proxy server details in a text file called settings.txt, URL Exceptions in a file called URLex.txt, and Network Exceptions in a file called netex.txt, which we placed in a folder called proxy within the document root of the website. These files will be read by PHP every time the wpad file is requested, and the wpad file will be written “on-the-fly” before it is sent to the user.

The content of the files are shown below.,, D, Internal Network.

The fields are separated by commas, and different entries are separated by lines. The Fields are , , , .

The Network, Subnet Mask, and Comment are all pretty self explanatory, but the action needs a little more explanation…. In order to add methods to both send traffic both direct, and via proxy, we needed a way to define which route the traffic should take. D stands for DIRECT, and P stands for PROXY.

The contents of urlex.txt:*, P, Example Website
http://**, D, Example Services
http://*.example.local*, D, Local Domain

This file is formatted as , , . The URL pattern can contain *’s as a wild card character.

The Contents of settings.txt:

0, PROXY, DIRECT, PROXY, Local Config


, , , . The P and D Action Destinations correspond to the options in the exceptions files. Default destination are where we send requests that do not match anything in the exceptions files.

Now, for the content of our wpad.dat file. Providing you have followed this post to the letter, and I haven’t missed anything out, your wpad system should work fine after this file has been populated.

$urlfile = "proxy/urlex.txt";
$netfile = "proxy/netex.txt";
$setfile = "proxy/settings.txt";
$sett = fopen($setfile, 'r');
$settings = explode(", ", fgets($sett));
$actions = array();
$actions['D'] = $settings[2];
$actions['P'] = $settings[1];
if($settings[0] == '1') { $function .= ' alert("Debug Mode Enabled!"); '; }
$function = ' function FindProxyForURL(url, host) { ';
if($settings[0] == '1') { $function .= ' alert("Reading URL Exceptions List"); '; }
$fh = fopen($urlfile, 'r');
$ln = 0;
while ($line= fgets ($fh)) {
$split_point =', ';
$split_string = explode($split_point, $line);
$function .= ' if (shExpMatch(url, "'.$split_string[0].'")) {';
if($settings[0] == '1') { $function .= ' alert("URL Match Found: '.$split_string[0].'"); '; }
$function .= ' return "'.$actions[$split_string[1]].'"; } ';
$fh = fopen($netfile, 'r');
$ln = 0;
while ($line = fgets ($fh)) {
$split_point =', ';
$split_string = explode($split_point, $line);
$function .= ' if (isInNet(host, "'.$split_string[0].'", "'.$split_string[1].'")) {';
if($settings[0] == '1') { $function .= ' alert("Network Match Found: '.$split_string[0].'"); '; }
$function .= 'return "'.$actions[$split_string[2]].'"; } ';
if($settings[0] == '1') { $function .= ' alert("No Match Found - Drop to Default Path...."); '; }
$function .= 'return "'.$settings[3].'"; ';
$function .= ' }';
echo $function;

Basically this script will read the files and “echo” a bunch of compiled text that the web browser will interpret as JavaScript. Here is the output text of the script. It may not be as neat as a conventional PAC file, but your browser will still know what to do with it.

function FindProxyForURL(url, host) { if (shExpMatch(url, “*”)) { return “PROXY”; } if (shExpMatch(url, “http://**”)) { return “DIRECT”; } if (shExpMatch(url, “http://*.example.local*”)) { return “DIRECT”; } if (isInNet(host, “”, “”)) {return “DIRECT”; } return “PROXY”; }

I recommend a server reboot, followed by a client reboot before you test this system.

I hope you all find this script useful, I know there isn’t a great deal of information available online on this subject.

This script could also easily be expanded to use a database as the data source instead of text file. All it would take is a little bit of PHP and MySQL know how. Who Knows? I might even post something a bit more dynamic one day when I’m bored….

Please feel free to leave any comments, feedback, help requests or additional information in the comments, and I will do my best to help you out.