Category Archives: Network

Changing IP settings fast

When I need to keep changing TCP/IP settings to test networking configs it’s a real pain to have to keep opening up the adapter properties (especially on Windows 7) and usually I’m in too much of a hurry to lookup up the netsh command syntax. For that reason I’m posting this small script. Save it as ipset.cmd.

Note: using netsh to revert to DHCP seems to be intermittent if no DHCP server is available – e.g. if the adapter has no link.

Windows 7

@echo off
If "%1" == "" (
  echo Configures Local Area Connection
  echo   ipset address/maskbits gateway (dns)
  echo   ipset 192.168.1.99/24 192.168.1.254
  echo   ipset 192.168.1.99/24 192.168.1.254 8.8.8.8
  echo   ipset dhcp
  echo.
  goto :eof
)
If "%1" == "dhcp" (
  netsh interface ip set dnsservers name="Local Area Connection" source=%1
  netsh interface ip set address name="Local Area Connection" source=%1
  goto :eof
)
netsh interface ip set address name="Local Area Connection" source=static address=%1 gateway=%2
If "%3" == "" (
  :: OpenDNS public DNS servers
  netsh interface ip set dnsservers name="Local Area Connection" source=static address=208.67.222.222
  netsh interface ip add dnsservers name="Local Area Connection" address=208.67.222.220
) else (
  netsh interface ip set dnsservers name="Local Area Connection" source=static address=%3
)

Windows XP

@echo off
If "%1" == "" (
  echo Configures Local Area Connection
  echo   ipset address mask gateway
  echo   ipset 192.168.1.99 255.255.255.0 192.168.1.254
  echo   ipset dhcp
  echo.
  goto :eof
)
If "%1" == "dhcp" (
  netsh int ip set address local source=%1
  netsh int ip set dns local source=%1
  goto :eof
) else (
  netsh int ip set address local static %1 %2 %3 1
  :: OpenDNS public DNS servers
  netsh int ip add dns local 208.67.222.222 index=1
  netsh int ip add dns local 208.67.222.220 index=2
)
Advertisements

SIP with NAT on Netscreen Firewalls or: How I Learned to Stop Worrying and Love the ALG

The place where I work recently set up new additional business premises. The telephony system is a Mitel 3300 using SIP trunks directly from the cloud (Gamma Telecom). I’ve named the SIP provider only because this is likely to be important as a confirmed working example of Netscreen + Mitel + Gamma. This setup all seemed like a great idea until I wasted about a solid week of my time when it didn’t work as expected. A communications solutions provider designed and installed everything under contract but we supplied our own firewall – a Juniper Netscreen NS-50. Having used CheckPoint and Cisco PIX, I much prefer the Juniper for its decent GUI and instant policy changes (unlike CheckPoint). Our main motivation for using it was for consistency since we run another Netscreen at our main site.

Despite having many man-days of the comms company’s network specialists, Mitel specialists, dozens of packet traces, conversations with the SIP provider, and the reseller which supports our Netscreen, we made very little progress. The comms company were convinced the Netscreen was at fault despite never having flagged it up as a potential concern months earlier. Things were made more difficult by the lack of documented user experiences online which is why I’m writing this up. Many people using SIP tend to be using it between buildings, or to an internal gateway – not an external gateway on the public Internet. Also, it seems that other SIP-enabled PBXs can act as a media proxy which keeps things simple. However, although a Mitel PBX handles the call setup, each individual handset sends and receives media streams directly from the remote media gateway.

Can the Netscreen ALG translate Mitel 3300 SIP traffic? YES!

Despite most online opinions telling you to disable it, the Netscreen’s SIP ALG does indeed work correctly (running ScreenOS 5.4.0r16 in this case). Using an external SIP provider you absolutely will require a functioning ALG for inbound calls. Without it, you would need either a public IP for each handset in your organization, or a static translation in your firewall for each handset, or even a private tunnel to the SIP provider allowing them to route traffic for your private addresses. These aren’t very practical, though the last one can be implemented as a last ditch solution. I came very close to having to do that.

Before you start, read the ScreenOS 5.4 manual’s chapter on Voice over IP, particularly from page 23 onwards.

Configure your PBX as a MIP on the Untrust interface (typically Ethernet 3 on a Netscreen), making sure to create it on the trust-vr router (there’s a dropdown as you create the MIP).

Now create an Untrust to Trust policy as below. Leave the Application dropdown set to None (this will autoselect the most appropriate ALG – obviously SIP in this case):
Untrust-Trust firewall policy for SIP

Next create the Trust to Untrust policy defining the Source as network(s) which will include your PBX’s private address and those of all of your handsets. Widen the netmask if you need to. Note that I’ve had to include both the SIP and the Media gateways for the Destination:

Trust-Untrust firewall policy for SIP

From the outset the Netscreen was indeed translating IP addresses in the SDP fields of the packet datagrams, but the contractors were convinced that the ALG was faulty because it was trying to carry out call setup and media streaming on different IPs. As Gamma later pointed out – this is completely acceptable. Their endpoint does the same after all.

Though I was already doing this, according to Juniper KB7407 for the SIP ALG to work correctly you will need to use Policy-based NAT, not Interface-based – i.e. the Trust interface will need to be in Route mode, not NAT mode. Then for each Trust to Untrust policy you will need to click on Advanced and enable source translation like so:
Policy-based NAT

This is what gives you the blue policy action icon shown in the earlier screenshot. The firewall will NAT outbound SIP traffic on the MIP, but RTP media streams from the handsets will be NATed on the firewall egress interface (ethernet3). This is by design – don’t let anyone tell you this won’t work, because it does!

I can vouch for these settings, so if your test calls still aren’t connecting then it’s quite likely that either your firewall policy isn’t quite right (try temporarily relaxing the policies by adding some Any values), or the Mitel isn’t correctly configured. In my case I got Gamma Telecom to send over their best practice guide for the Mitel 3300 from their knowledgebase which revealed that a few settings had been set differently on ours including the contentious sounding Enable Mitel proprietary SDP which Gamma wanted disabled. Another crucial one is Suppress Use of SDP Inactive Media Streams – without it you won’t be able to transfer external calls to another handset. Their screenshot looked to be from an earlier release of the Mitel software so here is a view of our working settings taken from Trunks > SIP > SIP Peer Profile in the WebUI:

SIP peer profile settings

For inbound calls to your PBX, double-check how many digits of your DDI numbers the remote SIP endpoint is sending in the SIP message headers and configure the Mitel to match – we had a mismatch here with ours which again would have spoiled many of our earlier tests. Set the number of leading digits that get truncated off the incoming DDI to transform it into an internal extension number at Trunks > Trunk Attributes > Dial In Trunks Incoming Digit Modification – Absorb.

It’s worth stating that this Netscreen/Mitel configuration works with the default endpoint config at the Gamma Telecom end. During troubleshooting they changed many settings which may have complicated things even more, but I am told it has all been reset to the standard specification.

To debug the SIP ALG on the Netscreen download PuTTY and enable logging to a file. Then SSH into your Netscreen and type:

set dbuf size 512
clear dbuf
debug sip all

Now carry out your test call, then:

undebug all
get dbuf stream
set dbuf size 128

This will dump pages of output to the screen, too much for the buffer but now you can open the log file you told PuTTY to save.

There are so many variables to getting this to work, and you will most likely have to draw on the expertise of several different people, but the only way forward is careful methodical testing. Never change more than one thing at a time. In my case the working solution saw me use the exact same firewall settings I had used at the very beginning. The issues turned out to be solved by tweaking of the PBX and remote SIP gateway, though in the thick of it I also upgraded from ScreenOS 5.4.0r8 to 5.4.0r16. However, because the comms contractor kept sending different people to work on the problem the testing was not really consistent until it was just me alone dealing directly with Gamma Telecom support, in control of the firewall, and with WebUI access to the Mitel.

Good luck! If this page saves days of your life going stir crazy in a comms room, nights away at a remote site staying in a hotel etc., then I’d love to hear about it. As you can guess I wasn’t so lucky…

UPDATE – unfortunately transferring external calls is not currently possible with this setup. It looks like that ALG isn’t dealing correctly with the way the Mitel does this (a second INVITE, placing RTP stream on hold etc.).

UPDATE 2 – enabling the Mitel SIP Peer Profile setting Suppress Use of SDP Inactive Media Streams fixes call transfers with consultation (I updated the screenshot above). Blind call transfers still don’t work unfortunately.

Wake on LAN for VPN users

In the absence of a Terminal Server, having users remotely use the software on their desktop PCs is often easier than having to manage software packages on laptops which may be part of a generic pool. In an energy conscious business there is the problem of what to do if one of your remote users wants to get at their desktop PC while it’s asleep or powered down. Wake on LAN works by sending a magic packet – featuring the target PC’s MAC address – to the network broadcast address. A MAC address is pretty unwieldy, so the ideal solution is an intranet page allowing users to wake a PC by hostname once they’re connected to the VPN.

To do this you will need some kind of host database to link hostnames to MAC addresses (remember – the machines could be switched off, so the DHCP database is no good). Your intranet server must also be on the same subnet as the machines you intend to wake. I have only used this in a single subnet so I haven’t investigated scalability, but it just looks like a case of enabling directed broadcasts on your routers. My login script updates host database entries and collects other WMI info such as make & model, tag number, spec, etc.

I implemented this Wake on LAN four years ago so there may be neater ways of doing it by now. At the time I couldn’t do the whole thing in ASP because there was no free socket library for VBScript, so I used Perl to create the magic packet. I used a generic wakeonlan.pl script by José Pedro Oliveira and tweaked it to post back to the ASP page.

Here are the required scripts – the first is ASP part you would need for the Intranet page:

<% Language=VBScript %>
<p>Wake your PC to allow you to connect to it remotely.</p>
<form method="get" action="./default.asp">Name of PC to power on: <input name="hostname" maxlength=14><input type="submit" value="wake"><br>
<%
Dim strHostname
strHostname = Request.QueryString("hostname")

'check query string from form above
If Not strHostname = "" Then
  'remove potential SQL injection attack characters
  strHostname = killChars(strHostname)
  Dim strConnection, objConnection, objRecordSet, objCommand, objResult

  'create connection object
  strConnection = "Provider=SQLOLEDB; Data Source=sqlsvr.domain.com; Initial Catalog=HostDB;User Id=HostDB_RO;Password=yourpassword"
  Set objConnection = CreateObject("ADODB.Connection")
  objConnection.Open strConnection

  'create command object
  set objCommand = CreateObject("ADODB.Command")
  objCommand.ActiveConnection = objConnection

  'check to see if a MAC exists for this hostname - you'll need to customize this depending on your database
  objCommand.CommandText = "SELECT * FROM Inventory WHERE Hostname='" & strHostname & "'"
  set objRecordSet = objCommand.Execute
  If Not objRecordSet.EOF Then
    'if it does exist then wake it
    Response.Redirect ("./wakeonlan.plx?MAC=" & objRecordSet.Fields.Item("MAC").value)
  Else
    Response.Write ("<em>Unknown computer: '" & strHostname & "'.</em>")
  End If
  objRecordSet.Close
  Set objCommand = nothing
  objConnection.Close
  Set objConnection = nothing
End If

'check query string for result from wakeonlan.plx
Dim strResult, strMAC
strResult = Request.QueryString("result")
If strResult = "True" Then
  Response.Write("<em>Wake-up packet sent. Wait around one minute before connecting.</em>")
End If
If strResult = "False" Then
  Response.Write("<em>Invalid MAC.</em>")
End If

'sanitize against SQL injection attacks
Function killChars(strWords)
  Dim arrBadChars, strNewChars
  arrBadChars = array("select", "drop", ";", "--", "insert", "delete", "xp_", "'", "=", " ")
  strNewChars = strWords
  For i = 0 To uBound(arrBadChars)
    strNewChars = replace(strNewChars, arrBadChars(i), "")
  Next
  killChars = strNewChars
End Function
%>
</form>

And this is wakeonlan.plx:

#!/usr/bin/perl
#
# wakeonlan.plx
# based on José Pedro Oliveira's wakeonlan.pl v1.4.2.3 <jpo@di.uminho.pt>

use strict;
use Env "QUERY_STRING";
use Socket;

# your LAN broadcast address
my $DEFAULT_IP = '172.16.1.255';
my $DEFAULT_PORT = getservbyname('discard', 'udp');
my %FORM;
my $result;

&parse_query_string;
&wake($FORM{MAC});

print 'Status: 302 Moved', "\r\n", 'Location: ./default.asp?result=', $result, "\r\n\r\n";

sub parse_query_string {
  my ($buffer, @pairs, $pair, $name, $value);
  if (length ($ENV{'QUERY_STRING'}) > 0){
    $buffer = $ENV{'QUERY_STRING'};
    @pairs = split(/&/, $buffer);
    foreach $pair (@pairs){
      ($name, $value) = split(/=/, $pair);
      $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
      $FORM{$name} = $value;
    }
  }
}

sub wake {
  my $hwaddr  = shift;
  my $ipaddr  = $DEFAULT_IP;
  my $port    = $DEFAULT_PORT;
  my ($raddr, $them, $proto);
  my ($hwaddr_re, $pkt);

  # Validate hardware address (ethernet address)
  $hwaddr_re = join(':', ('[0-9A-Fa-f]{1,2}') x 6);
  if ($hwaddr !~ m/^$hwaddr_re$/) {
    $result = "False";
    return undef;
  }

  # Generate magic packet
  foreach (split /:/, $hwaddr) {
    $pkt .= chr(hex($_));
  }
  $pkt = chr(0xFF) x 6 . $pkt x 16;

  # Allocate socket and send packet
  $raddr = gethostbyname($ipaddr);
  $them = pack_sockaddr_in($port, $raddr);
  $proto = getprotobyname('udp');

  socket(S, AF_INET, SOCK_DGRAM, $proto) or die "socket : $!";
  setsockopt(S, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!";

  $result = "True";

  send(S, $pkt, 0, $them) or die "send : $!";
  close S;
}